导图社区 Java程序设计
Java编程语言基础语法与高级应用,包括基础语法,网络编程,高并发性编程。
编辑于2021-01-24 19:47:08Java程序设计
Java简介及开发环境
Java语言的产生
起源于sun公司的"Green"项目,研究方向为:智能家电
基于C++开发的一种新的Oak语言
1995年正式更名为Java
Java之父
詹姆斯·高斯林
Java版本
J2SE
Java SE:99年发布的标准版,为EE提供基础支持
J2EE
Java EE:企业版,健壮、安全的服务器端程序。提供WEB服务
J2ME
Java ME:微型版,为移动设备和嵌入式设备上运行的应用程序的环境
Java是纯面向对象语言
对象
类
封装
抽象
继承
多态性
Java的功能特点
简单
面向对象
可移植性
与平台无关性
安全性
健壮性
体系结构中立
原始类型的大小是固定的
动态性
解释执行
高性能
多线程
分布式
Java的运行机制

Java的两种机制
Java的虚拟机机制(JVM)
虚拟机机制保证Java程序的跨平台特性
Java的垃圾回收机制
垃圾回收机制保证Java程序更安全、更高效
Java虚拟机
Java虚拟机是可运行Java字节码的虚拟计算机系统
使用Java编写的程序,实际上是运行在JVM上,而不是运行在操作系统上
有一个解释器组件,可以实现Java字节码和计算机操作系统之间的通信

运行过程

开发Java的准备
安装JDK
如果只需运行Java程序,只需安装JRE,如要进行开发编译JDK也要装

安装时要注意安装路径
JDK子文件夹
bin
存放可执行文件
lib
存放Java的类库文件
include
存放用于本地方法的文件
配置PATH环境变量



win+R输入cmd;
第一个Java程序
1.打开Eclipse,选择File->Java Project->输入相关信息



2.创建好相关的文件夹后,在src中新建文件HelloWorld.java


3.编辑HelloWorld.java文件

4.选择Run->Select All即可查看到相关的输出信息
输出:Hello World!
Java程序结构分析
1.class关键字用于在Java中声明一个类
2.public关键字时表示可见性的访问修饰符,表示所有人可见
3.static是关键字,如果将某个方法声明为static,它被称为静态方法。静态方法的核心优势是不用创建对象就可以直接调用。main方法由JVM执行,因此不需要创建对象来调用。
4.void是方法的返回类型,它意味着它不返回任何类型
5.main表示程序开始(执行的入口)
6.String []args用于命令行参数。
7.system.out.println()是打印输出语句
几种有效的Java main方法写法
public static void main(String[] args)
public static void main(String []args)
public static void main(String args[])
public static void main(String... args)
static public void main(String[] args)
public static final void main(String[] args)
final public static void main(String[] args)
应用要点
一个源文件至多只能有一个public的class声明
源文件名必须和它定义的public的类名相同
main方法是Java应用程序的入口方法
Java命令执行Java程序时,不需带扩展名
args字符串数组,存储输入参数

Java基础语法
Java数据类型
Java属于强类型语言,每个变量声明时必须执行一种类型
Java程序中所处理的数据是将各种数据类型实例化的数据
数据类型实例化的方法
说明语句
实例化数据的两种形式
常量
用关键字final指示常量
final表示这个变量只能被赋值一次,一旦被赋值后,就不能再更改了。
变量
数据类型
简单类型
Java语言数据中的数值类型都是有符号(正负号)的,在贮存数值类型的数据时,其最高位用来表示数据的正负号
简单类型的变量被声明时,存储空间也同时被分配。该贮存空间只占用一个单一贮存单元。对简单类型变量访问则直接可以得到它的数据。
整数类型
byte
存储
8位
最小值
-128
最大值
+127
short
存储
16位
最小值
最大值
int
存储
32位
最小值
最大值
long
存储
64位
最小值
最大值
整型数据用来表示整数,长整型数值有一个后缀L或l
通常情况下,int型应用最多,如果要表示特别大的数,就要使用long类型。
而byte和short类型主要用于特定的场合。
Java中可用三种进制来表示整数
二进制
二进制前缀0b或0B
int a=0b111000;
十进制
int x=678;
八进制
八进制前缀0
int y=0456;
十六进制
十六进制前缀0x或0X
int z=0xa9bc;
浮点类型
float
存储
32位
最小值
大约-3.40282347E+38F(有效位数为6--7位)
最大值
大约3.40282347E+38F(有效位数为6--7位)
double
存储
64位
最小值
大约-1.79769313486231570E+308(有效位数为15位)
最大值
大约1.79769313486231570E+308(有效位数为15位)
浮点型表示有小数部分的数值。
浮点数型数据默认为double型,在使用float型字面常量时,必须添加后缀F或f。没有添加F后缀会默认为double类型。
float x=12.345;错误
float x=12.345f;正确
使用double型,可以添加后缀D或d,也可不添加
double d=12.3456789;正确
double d=12.3456789D;正确
字符类型
char
存储
16位
最小值
Unicode:0
最大值
char型用来表示字符,每个char型变量占两个字节,在Java中,字符的编码是采用Unicode编码
Unicode编码字符使用16位无符号整数表示,有2^16个可能值,及0~65535。可以表示世界上的大部分文字语言的字符。
一般情况下,char类型数据被表示用一对单引号包含的单个字符。
char c1='n';
char c2='@';
转义字符

布尔类型
boolean
存储
1bit
最小值
false
最大值
true
boolean型的取值范围非true即false,用来表示逻辑值的真或假。其字面常量也只有true和false两种选择。整型值和布尔值之间不能进行相互转换。
boolean b1=true;
boolean b2=false;
在C++中用数值或指针可以代替boolean值,0相当于false,非0相当于true
在Java中,boolean型的值不能与其他任何基本类型的值进行转换代替
大数值
如果基本的整数和浮点数精度不能满足需求,就可以使用java.math包中的两个类:
BigInteger
实现了任意精度的整数运算
BigDecimal
实现了任意精度的浮点数运算。
使用静态的valueOf方法可以将普通的数值转换为大数值:
BigInteger a=BigInteger.valueOf(100);
不能用算术运算符处理大数值,需要使用大数值类的add和multiply方法。

API
java.math.BigInteger
BigInteger add(BigInteger other)
BigInteger subtract(BigInteger other)
BigInteger multiply(BigInteger other)
BigInteger divide(BigInteger other)
BigInteger mod(BigInteger other)
返回这个大整数和另一个大整数other的和差积商以及余数。
int compareTo(BigInteger other)
如果这个大整数与另一个大整数other相等,返回0;如果这个大整数小于另一个大整数other,返回负数;否则,返回正数。
static BigInteger valueOf(long x)
返回值等于x的大整数
java.math.BigDecimal
BigDecimal add(BigDecimal other)
BigDecimal subtract(BigDecimal other)
BigDecimal multiply(BigDecimal other)
BigDecimal divide(BigDecimal other RoundingMode mode)
返回这个大实数与另一个大实数other的和差积商。要计算商,必须给出舍入方式。RoundingMode.HALF_UP就是四舍五入方式,适用于常规计算。
int compareTo(BigDecimal other)
如果这个大实数与另一个大实数相等,返回0;如果这个大实数小于另一个大实数,返回负数,否则,返回正数。
static BigDecimal valueOf(long x)
static BigDecimal valueOf(long x,int scale)
返回值为x或x/(10^scale)的一个大实数。
Java基本数据类型声明
boolean b=true;
int i,j,k=-99;
long l=1234456677;
char ch='J';
char chr='中';
float f=3.1415826f;
double d=-1.14E-5;
String s="信息工程";
注意
声明局部变量后必须初始化后才可以使用


基本数据之间的类型转换
自动转换
自动转换按从低到高的顺序转换
不同类型数据之间的优先关系如下:

运算中,不同类型的数据先转化为同一类型,然后进行运算,转换规则如下:

强制类型转换
转换格式是在需要转换的数据前加上"()",然后在括号中加入需要转化的数据类型。
有的数据转换后,精度会丢失,有的会更精确。

输出结果为:

数值之间的转换

实线箭头
表示无信息丢失的转换
虚线箭头
表示可能有精度损失的转换
当使用两个数值进行二元操作时(例如n+f,n是整数,f是浮点数)
必须先将两个操作数转换为同一种类型,然后再进行计算。
如果两个操作数中有一个是double,另一个操作数就会转换为double类型。
如果其中一个操作数是float类型,另一个操作数将会转换为float类型。
如果其中一个操作说是long类型,另一个操作数将会转换为long类型。
否则,两个操作数都将被转换为int类型。
引用类型
数组
<type>[]
类
class
枚举
枚举类型包括有限个命名的值。
例子

声明枚举类型后,可以按如下方式声明变量:

Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值,null表示这个变量没有设置任何值。
接口
interface
变量类型

变量的声明与初始化
声明
每个声明必须以分号结束。
变量名必须是一个以字母开头并由字母或数字构成的序列。
注意:
变量名对大小写敏感。
不能使用Java保留字作为变量名。
初始化
声明一个变量后,必须对其进行初始化。
对一个声明过的变量进行赋值,必须将变量名放在等号左侧:

局部变量
声明在方法、构造方法或者语句块中。
在方法、构造方法或者语句块被执行时创建,当任务执行完成后,变量将会被销毁。
访问修饰符不能用于局部变量。
局部变量只在声明它的方法、构造方法或者语句块中可见。
局部变量是在栈上分配。
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
例子

实例变量
声明在一个类中,但在方法、构造方法和语句块之外
当一个对象被实例化之后,每个实例变量的值就跟着确定了
实例变量在对象创建的时候创建,在对象被销毁的时候销毁
例子

类变量
也称为静态变量,在类中以static关键字声明,必须在方法、构造方法和语句块之外。
无论一个类创建多少个对象,类只有类变量的一份拷贝
静态变量在程序开始时创建,在程序结束时销毁
大多数静态变量声明为public类型
例子

运算符
功能分类
结合赋值
在赋值中使用二元运算符,这是一种简写形式:
x+=4;等价于x=x+4;
如果运算符得到一个值,其类型与左侧操作数的类型不同,就会发生强制类型转换。
算术运算符
+
num1+num2
num1加num2
-
num1-num2
num1减num2
*
num1*num2
num1乘num2
/
num1*num2
num1除num2;num2不等于0
%
num1%num2
num1除以num2的余数
++
++a或a++
自增:操作数的值增加1
--
--a或a--
自减:操作数的值减少1
例子


自增自减运算符
前缀自增自减法(++a,--a):
先进行自增或者自减运算,再进行表达式运算
后缀自增自减法(a++,a--):
先进行表达式运算,再进行自增或自减运算
例子


关系运算符
>
num1>num2
大于
若num1>num2,为true,否则为false
>=
num1>=num2
大于等于
若num1>=num2,为true,否则为false
<
num1<num2
小于
若num1<num2,为true,否则为false
<=
num1<=num2
小于等于
若num1<=num2,为true,否则为false
==
num1==num2
相等
若num1==num2,为true,否则为false
!=
num1!=num2
不相等
若num1!=num2,为true,否则为false
例子


布尔运算符
!
!opB1
非
若opB1是true则为false,是false则为true
&&
opB1 && opB2
与
若opB1,opB2全true则为ture,有false则false
如果opB1值为false,则运算式的值是false,无论opB2的值是什么,不会访问opB2
如果opB1值为true,则需要opB2的值才能确定运算式的值,程序需要访问opB2
||
opB1 || opB2
或
若opB1,opB2全false则为false,有true则ture
如果opB1值为true,则运算式的值就是true,无论opB2的值是什么,不会访问opB2
如果opB1值为false,则需要opB2的值才能确定运算式的值,程序需要访问opB2
例子


位运算符
位运算符是对数据的二进制位操作,位运算符的操作数只能是整型的数据。
移位操作
逻辑运算
右移位运算符
>>
opBt1 >> opBt2
opBt1右移opBt2位
>>>
opBt1 >>> opBt2
opBt1无符号右移opBt2位
用0填充高位

左移位运算符
执行一个左移位运算符。移位的结果是第一个操作数乘以2的幂,而这个幂的指数就是第二个操作数
左移位时,高位被截去,低位填充0。
<<
opBt1 << opBt2
opBt1左移opBt2位

&
opBt1 & opBt2
opBt1和opBt2按位与

|
opBt1 | opBt2
opBt1和opBt2按位或

^
opBt1 ^ opBt2
opBt1和opBt2按位异或

~
~opBt1
opBt1按位取反

例子


赋值运算符
=
简单的赋值运算符,将右操作数赋给左操作数
C=A+B将A+B的值赋给C
+=
加和赋值操作符,将左操作数和右操作数相加赋值给左操作数
C+=A等价于C=C+A
-=
减和赋值操作符,将左操作数和右操作数相减赋值给右操作数
C-=A等价于C=C-A
*=
乘和赋值操作符,将左操作数和右操作数相乘赋值给右操作数
C*=A等价于C=C*A
/=
除和赋值操作符,将左操作数和右操作数相除赋值给左操作数
C/=A等价于C=C/A
(%)=
取模和赋值操作符,将左操作数和右操作数取模后赋值给左操作数
C%=A等价于C=C%A
<<=
左移位赋值运算符
C<<=2等价于C=C<<2
>>=
右移位赋值运算符
C>>=2等价于C=C>>2
&=
按位与赋值运算符
C&=2等价于C=C&2
^=
按位异或赋值操作符
C^=2等价于C=C^2
|=
按位或赋值操作符
C|=2等价于C=C|2
例子


条件运算符
?:
三元运算符,有三个操作数,需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量
variable x = (expression) ? value if true : value if false
例子


其他运算符
下标
[]
实例
instanceof
内存分配
new
强制类型转换
(数据类型)
方法调用
()
操作数分类
单目(一元)运算符
双目(二元)运算符
三目(三元)运算符
运算符优先级

输入/输出
读取输入
打印输出到标准输出流,只需调用System.out.pprintln即可。
然而,读取标准输入流:
首先需要构造一个Scanner对象,并与标准输入流System.in关联
Scanner in=new Scanner(System.in);
然后就可以使用Scanner类的各种方法实现输入操作。
System.out.print("name:"); String name=in.nextLine();
这里使用nextLine方法是因为在输入行中有可能包含空格。
要想读取一个单词(以空白符作为分隔符),就调用
String firstName=in.next();
要想读取一个整数,就调用nextInt
System.out.print("age:"); int age=in.nextInt();
要想读取一个浮点数,调用nextDouble方法
Scanner类定义在java.util包中。
常用输入API
java.util.Scanner
Scanner(InputStream in)
用给定的输入流创建一个Scanner对象
String nextLine()
读取输入的下一行内容。
String next()
读取输入的下一个单词(以空格作为分隔符)
int nextInt()
double nextDouble()
读取并转换下一个表示整数或浮点数的字符序列
boolean hasNext()
检测输入中是否还有其他单词
boolean hasNextInt()
boolean hasNextDoubel()
检测是否还有表示整数或浮点数的下一个字符序列
java.lang.System
static Console console()
如果有可能进行交互操作,就通过控制台窗口为交互的用户返回一个Console对象,否则返回null。对于任何一个通过控制台窗口启动的程序,都可使用Console对象,否则,其可用性将与所使用的系统有关。
java.io.Console
static char[] readPassword(String prompt,Object...args)
static String readLine(String prompt,Object...args)
显示字符串prompt并且读取用户输入,知道输入航结束。args参数可以用来提供输入格式。
格式化输出
可以使用System.out.print(x)将数值x输出到控制台上
也可以使用System.out.printf("%8.2f",x);
可以用8个字符的宽度和小数点后两个字符的精度打印x。
也可以使用System.out.printf("%f,%d,%s",a,b,c);
可以使用多个参数。
printf的转换符

printf的标志

可以使用s转换符格式化任意的对象。
对于任意实现了Formattable接口的对象都将调用formatTo方法;
否则将调用toString方法,它可以将对象转换为字符串。
printf的日期和时间的转换符

提示
参数索引值从1开始,而不是从0开始,%1$...对第一个参数格式化。
例子
采用一些格式化的字符串指出要被格式化的参数索引。索引必须紧跟在%后面,并以$终止。
System.out.printf("%1$s %2$tB %2$te,%2$tY","Due date:",new Date());

还可以使用<标志,它指示前面格式说明中的参数将被再次使用。

格式说明符的语法图

使用静态的String.format方法创建一个格式化的字符串,而不打印输出:
String message=String.format("%s,%d",name,age);
文件的输入/输出
对文件进行读取,就需要一个用File对象构造一个Scanner对象。
例子

如果文件名中包含反斜杠符号,就要栽每个反斜杠之前再加一个额外的反斜杠:"C:\\dic\\myfile.txt"
对文件进行写入,需要构造一个PrintWriter对象。

如果文件不存在,就创建该文件,可以像输出到System.out一样使用print、println以及printf命令。
API
java.util.Scanner
Scanner(File f)
构造一个从给定文件读取数据的Scanner
Scanner(String data)
构造一个从给定字符串读取数据的Scanner
java.io.PrintWriter
PrintWriter(String fileName)
构造一个将数据写入文件的PrintWriter。文件名由参数指定。
java.nio.file.Paths
static Path get(String pathname)
根据给定的路径名构造一个Path。
Java流程控制
流程控制语句是用来控制程序中各语句执行顺序的语句,可以把单个语句组合成能完成一定功能的小逻辑模块。
块作用域
块(复合语句)
是指由一对大括号括起来的若干条简单的Java语句。
块确定了变量的作用域。
一个块可以嵌套在另一个块中。

但是不能在乔涛的两个块中声明同名的变量

其流程控制方式采用结构化程序设计中规定的三种基本流程结构
顺序结构
分支结构
双分支的if语句
一般形式
结构

例子

示意图


条件表达式是用来判断程序的走向,如果表达式值为真,则执行if分支的语句块,否则执行else分支的语句块
也可以不书写else分支,若表达式为假,则绕过if分支直接执行if语句后面的其他语句。
例子


多重嵌套的if语句
结构

例子

示意图

其执行控制是从上到下,对条件进行检测。当某个条件为真时,就执行与此条件有关的语句,而且越过阶梯的其余部分,若无一条件为真,则执行最后一个else语句。
例子




多分支开关语句switch

switch语句在执行时,首先计算表达式的值,这个值必须是整型或字符型。同时应与各个case分支的判断值的类型相一致。
计算出表达式值后,依次与各case语句相比较,相同则执行相应的分支语句,若不相同则执行default分支,若无default分支,则跳出整个switch语句。
switch语句中,可以若干个判断值共享一个分支,就可以实现由多个不同的判断语句流入相同的分支。
例子


当变量的值与case语句的值相等时,那么case语句之后的语句开始执行,直到break语句出现才会跳出switch语句。
当遇到break语句时,switch语句终止。程序跳转到switch语句后面的语句执行。case语句不必须要包含break语句。如果没有break语句,程序会继续执行下一条case语句,直到出现break语句。
switch语句可以包含一个default分支,该分支必须是switch语句的最后一个分支。default在没有case语句的值和变量值相等的时候执行,default分支不需要break语句。
循环结构
条件表达式的返回值都是布尔型,循环体可以是单个语句,也可以是复合语句
while语句
结构

例子

示意图

while语句先判断条件表达式的值,若为真,则执行循环体,若为假,跳出循环,执行while语句后面的语句。
例子


do-while语句

do-while语句限制性一次循环体后,再判断条件表达式的值,若为真,则执行循环体;若为假,跳出循环,执行do-while语句后面的语句
例子


for语句
结构

表达式1用来完成变量初始化工作
表达式2用来返回布尔值的条件表达式
表达式3用来修改循环变量
例子

示意图

例子


注意
在循环中,检测两个浮点数是否相等需要格外小心
例子
for(double x=0;x!=10;x+=0.1)...
这个循环可能永远不会结束。由于舍入的误差,最终可能得不到精确值。
因为0.1无法精确地用二进制表示。
for循环的变量作用域
当在for语句的第一部分中声明一个变量后,这个变量的作用域就是for循环的整个循环体。

如果在for语句内部定义一个变量,这个变量就不能再循环体外使用;如果要在for循环体外使用该变量,就要确保变量在循环语句的前面且在外部。

可以再各自独立的不同for循环中定义同名的变量

跳转语句
continue语句
必须用于循环结构,两种形式
一种是不带标号的continue语句,作用是终止当前这一轮的循环,跳过本轮剩余语句,直接进入当前循环的下一轮
另一种是带标号的continue语句
格式
continue 标号名
标号名应该定义在程序中外层循环语句的前面,用来标志这个循环结构
例子


break语句
作用是是程序的流程从一个语句块内部或循环体内部跳转出来
常用的是不带标号的,用于switch语句或其他循环语句
另一种是带标号的
格式
break 标号名;
标号名应该用来标志某个语句块。执行break语句就从这个语句块中跳出来,流程进入其后面的语句。
可以将标签应用到任何语句中,甚至可以应用到if语句或者块语句中。

例子


return语句
格式
return 表达式
用来使程序流程从方法调用中返回,表达式的值就是调用方法的返回值。
数组
概念
由数目固定的、相同类型的元素组成的有序集合,每个元素相当于一个变量
在Java中,数组是最简单的复合数据类型
数组元素可以是任意类型,可以是简单类型,也可以是引用类型。
访问数组中的某个元素,可以通过数组名加下标的形式
a[4]
数组下标的个数就是数组的维数:有一个下标就是一维数组,有两个下标就是二维数组,依次类推。
使用数组四个步骤
1.声明数组
int[] a;
声明数组是不用规定数组长度
int a[];
告诉计算机数据类型是什么
格式
数据类型 数组名[];
数据类型[] 数组名;
2.分配空间
a=new int[5];
告诉计算机分配几个连续的空间
格式
数据类型[] 数组名=new 数据类型[大小];
3.赋值
例子
a[0]=3;
向分配的格子里放数据
数组赋值有两种方式
边声明边赋值(静态初始化)
int[] score={89,90,91};
int score=new int[]{89,90,91}
注意:红色括号内不能指定数组长度
静态初始化必须在声明数组时进行,而不能写成int a[ ];a[ ]={89,79,76};或int a[ ] = new int[3];a={89, 79, 76};
动态地从键盘录入信息并赋值

4.处理数据

访问数组成员:使用"标识符[下标]"

length:数组的length属性
score[i]:访问成员
例子




for each循环
for each循环可以用来依次处理数组中的每个元素而不必为指定下标值而分心。
格式

for each访问数组示例


打印数组中的所有元素
利用Arrays类的toString方法。调用Arrays.toString(a),返回一个包含数组元素的字符串,这些元素被防止在括号内,并用逗号分隔。
例子
[2,3,5,7,11,13],打印此数组,可以调用
System.out.println(Arrays.toString(a));
数组排序
对数值型数组进行排序,可以使用Arrays类中的sort方法;

这个方法使用了优化的快速排序算法。
例子
选择一个随机的数值集合,首先将数值1,2,3...存入数组numbers中。


Arrays类的API
java.util.Arrays
static String toString(type[] a)
返回包含a中数据元素的字符串,这些数据元素被放在括号内,并用逗号分隔。
参数a的类型为int、long、short、char、byte、boolean、float或double的数组。
static type copyOf(type[] a,int length)
static type copyOfRange(type[] a,int start,int end)
返回与a类型相同的一个数组,其长度为length或者end-start,数组元素为a的值。 参数:a 类型为int、long、short、char、byte、boolean、float或double的数组。 start 起始下标(包含这个值)。 end 终止下标(不包含这个值)这个值可能大于a.length。在这种情况下,结果为0或false。 length 拷贝的数据元素长度,如果length值大于a.length,结果为0或false;否则,数组中只有前面length个数据元素的拷贝值。
static void sort(type[] a)
采用优化的快速排序算法对数组进行排序。 参数a:类型为int、long、short、char、byte、boolean、float或double的数组。
static int binarySearch(type[] a,type v)
static int binarySearch(type[] a,int start,int end,type v)
采用二分搜索算法查找值v,如果查找成功,则返回相应的下标值;否则返回一个负数值r。-r-1是为了保持a有序v应插入的位置。 参数:a 类型为int、long、short、char、byte、boolean、float或double的有序数组。 start 起始下标(包含这个值)。 end 终止下标(不包含这个值) v 同a的数据元素类型相同的值。
static void fill(type[] a,type v)
将数组的所有数据元素值设置为v。 参数a: 类型为int、short、chat、byte、boolean、float或double的数组。 参数v: 与a数据元素类型相同的值。
static boolean equals(type[] a,type[] b)
如果两个数组大小相同,并且下标相同的元素都对应相等,返回true。 参数a、b: 类型为int、long、short、char、byte、boolean、float或double的两个数组。
数组作为函数参数和返回值


数组边界
数组类中有唯一的成员变量:length,用来表示数组的长度,即数组元素的个数。
在Java中,数组的下标从0开始,到length-1结束,及数组a的第一个元素是a[0],最后的元素是a[length-1]。
与C/C++不同,Java对数组元素要进行越界检查。
使用new创建数组对象时,自动给length赋值,数组一旦创建完毕,length就确定下来,除非重新使用new创建新的对象。
程序在运行过程中会对length进行检查,若发生越界访问,则会抛出一个异常
二维数组
数组的元素可以是任何已存在的类型(包括数组)
一个一维数组可以作为另一个一维数组的元素
元素是一维数组的数组被称为二维数组。

二维数组的创建
格式
类型名[][] 数组名=new 类型名[][]
例如
int[][] a=new int[3][4]
意义是:a引用了一个其元素类型为int[4]的数组,a的长度是3.
或者说,数组a有3个元素,每个元素都是一个长度为4的一维整型数组。
二维数组的初始化
格式
类型名[][] 数组名={{初值n1,...初值nm},{...},...};
例如
int[][] a={{1,2,3},{4,5,6},{7,8,9}};
注意
a[i]是数组a中第i个一维数组的数组名
而a[i][j]是数组a中第i个一维数组中的第j个元素的变量名。
二维数组每一维的大小允许不同
由于把二维数组看做是数组的数组,并且二维数组每一维的大小可以不同。
int intArray[ ][ ] = { {1,2}, {2,3}, {3,4,5} };
int a[ ][ ] = new int[2][ ];
a[0]= new int[3];
二维数组的应用
矩阵的简单处理


a.length的值是数组a的长度
a[i].length的值是数组a中第i行的长度
要想快速打印一个二维数组的数据元素列表,可以调用
System.out.println(Arrays.deepToString(a));
输出格式为

不规则数组
Java实际上没有多维数组,只有一维数组,多维数组被解释为“数组的数组”。
例子
二维数组

表达式balances[i]引用第i个子数组,也就是二维表的第i行。它本身也是一个数组。balances[i][j]引用这个数组的第j项。
由于可以单独地存取数组的某一行,所以可以让两行交换。

可以方便地构造一个"不规则数组",即数组的每一行有不同的长度。
例子
创建一个数组,第i行第j列将存放"从i个数值中抽取j个数值"

由于j不可能大于i,所以矩阵是三角形的

深入理解数组变量是引用变量


数组变量拷贝复制
Java中,允许将一个数组变量拷贝给另一个数组变量,这两个变量将引用同一个数组:
将一个数组的所有值拷贝到新数组中,使用Arrays类的copyOf方法:
int[] luckyNumbers=smallPrimes; lucky[5]=12; int[] copyNum=Arrays.copyOf(luckyNumbers,luckyNumbers.length);

上述例子的新数组的第二个参数luckyNumbers.length是新数组的长度,通常用来增加数组的长度:
luckyNmubers=Arrays.copyOf(luckyNumbers,2*luckyNumbers.length);
如果数组元素是数值型的,那么多余的元素将被赋值为0;如果数组元素是布尔型,则将赋值为false;如果长度小于原始数组的长度,则只拷贝最前面的数据元素。



Java字符串
创建字符串
字符串数据实际上是由String类所实现的。
实际上分两类
一类是在程序中不会被改变长度的不变字符串
二类是在程序中会被改变长度的可变字符串
Java环境为了存储和维护这两类字符串提供了String和StringBuffer两个类
String str=new("This is a String!")
String str="This is a String!"
例子




创建格式化字符串
Java输出格式化数字可以使用printf()和format()方法
String类使用静态方法format()返回一个String对象而不是PrintStream对象
String类的静态方法format()能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。




不可变字符串
例子
将"Help!"中的最后两个位置的字符修改为"A"和"B"。
首先提取需要的字符,然后再拼接上替换的字符串。

上述代码就将"help!"修改为"helAB"。
由于不能修改Java字符串中的字符,所以在Java中将String类对象称为不可变字符串。
空串与Null串
空串
空串("")是长度为0的字符串。
检验一个字符串是否为空:
if(str.length()==0)
if(str.equals(""))
空串是一个Java对象,有自己的串长度(0)和内容(空)。
Null串
String变量存放的一个特殊值,名为null。
表示目前没有任何对象与该变量关联。
要检查一个字符串是否为空,使用以下条件:
if(str==null)
检查一个字符串既不是null也不是空串,使用以下条件:
if(str!=null && str.length()!=0)
首先要检查str不为null。
码点与代码单元
码点
Java字符串由char值序列组成。
length方法将返回UTF-16编码表示的给定字符串所需要的代码单元数量。
例子

要得到上面字符串的实际长度(码点数量),可以调用:

调用s.charAt(n)将返回位置n的代码单元,n介于0--s.length()-1之间。

如果要得到上述字符串的第i个码点,应该使用下列语句:

代码单元
例子
⑪is the set of octonions
如果用UTF-16编码表示上述第一个字符⑪(U+1D546)需要两个代码单元。必须调用下面语句:char ch=sentence.chatAt(1)
返回的不是一个空格,而是⑪的第二个代码单元。为了避免这个问题,不要使用char类型。
遍历字符串
如果要遍历一个字符串,并且一次查看每一个码点,可以使用下列语句:

可以使用下列语句实现回退操作:

更容易的办法是使用codePoints方法
codePoints方法会生成一个int值的流,每个int值对应一个码点。可以将它转换成一个数组,再完成遍历。

要将一个码点数组转换为一个字符串,可以使用构造函数:

字符串的常用操作
创建字符串的连接操作
字符串可以通过"+"连接,基本数据类型与字符串进行"+"操作,一般也会自动转换为字符串


求字符串对象的相关信息
通过调用length()方法得到String的长度。
String str="This is a String!";
int len=str.length();
如果想确定字符串中指定字符或子字符串在给定字符串的位置
indexOf(subString[, startIndex])
lastIndexOf(subString)
例子
String str="This is a String.";
int index1=str.indexOf("i");
intindex2=str.indexOf("i",index1+1);
intindex3=str.lastIndexOf("i");
intindex4=str.indexOf("String");
字符串的比较
String类的equals()方法用来确定两个字符串是否相等
String str="This is a String";
Boolean result=str.equals("This is another String");
如果两个字符串相等,则返回true;否则返回false。
equalslgnoreCase()方法可以检测两个字符串是否相等,不区分大小写。
String str="This is a String";
Boolean result=str.equalslgnoreCase("This is another String");
如果两个字符串相等,则返回true;否则返回false。
“==”运算符只能检测两个字符串是否放置在同一个位置上,其他相关信息均不检测。
字符串的访问
方法charAt()用以得到指定位置的字符
String str="Thisisa String";
char chr=str.charAt(3);
方法getChars()用以得到字符串的一部分字符串
public void getChars(intsrcBegin,intsrcEnd,char[]dst,intdstBegin)
String str="ThisisaString";
Char chr=new char[10];
Str.getChars(5,12,chr,0);
System.out.println(new String(chr));
输出:saStrin
方法Substring求子字符串
public String substring(intbeginIndex, intendIndex)
第一个int为开始的索引,对应String数字中的开始位置
第二个int是截止的索引位置,对应String中的结束位置
取得的字符串长度为:endIndex-beginIndex;
从beginIndex开始取,到endIndex结束,从0开始数,其中不包括endIndex位置的字符
String str="ThisisaString";
String str1=str.substring(1,4);
System.out.println(str1);
输出his
优点
容易计算子串的长度。字符串s.substring(a,b)的长度b-a。
字符串的其他操作
replace()
可以将字符串中的一个字符替换为另一个字符
例子
String str="ThisisaString";
String str1=str.replace('T','t');
concat()
可以把两个字符串合并为一个字符串
例子
String str="Thisisa String";
String str1=str.concat("Test");
toUpperCase()
实现字符串的大写转换
例子
String str="THISISASTRING";
String str1=str.toLowerCase();
toLowCase()
实现字符串的小写转换
例子
String str="THISISASTRING";
String str1=str.toLowerCase();
trim()
可以将字符串中开头和结尾处的空格去掉
例子
String str=" This is a String ";
String str1=str.trim();
valueOf()
静态方法,可以将任何类型的数据对象转换为一个字符串。
例子
System.out.println( String.valueOf(Math.PI) );
split()
以指定字符串作为分隔符,对当前字符串进行分割,分割的结果存放在一个数组中
例子


修改可变字符串
在字符串后面追加
用append()方法将各种对象加入到字符串中
在字符串中间插入
用insert()方法
改变某个位置所在的字符
用setCharAt()方法
例子




StringBuffer与String效率比较


String API
java.lang.string
char charAt(int index)
返回给定位置的代码单元。
int codePointAt(int index)
返回从给定位置开始的码点
int offsetByCodePoints(int startIndex,int cpCount)
返回从startIndex代码点开始,位移cpCount后的码点索引
int compareTo(String other)
按照字典顺序,如果字符串位于other之前,返回一个负数;
如果字符串位于other之后,返回一个正数;
如果两个字符串相等,返回0。
IntStream codePoints()
将这个字符串的码点作为一个流返回。调用toArray将它们放在一个数组中。
new String(int[] codePoints,int offset,int count)
用数组中从offset开始的count个码点构造一个字符串。
boolean equals(Object other)
如果字符串与other相等,返回true。
boolean equalsIgnoreCase(String other)
如果字符串与other相等(忽略大小写),返回true。
boolean startsWith(String prefix)
如果字符串是以prefix开头的,则返回true
boolean endsWith(String suffix)
如果字符串是以suffix结尾的,则返回true
int indexOf(String str)
int indexOf(String str,int fromIndex)
int indexOf(int cp)
int indexOf(int cp,int fromIndex)
返回与字符串str或代码点cp匹配的第一个子串的开始位置。这个位置从索引0或fromIndex开始计算,如果在原始串中不存在str,返回-1。
int lastIndexOf(String str)
int lastIndexOf(String str,int fromIndex)
int lastIndexOf(int cp)
int lastIndexOf(int cp,int fromIndex)
返回与字符串str或代码点cp匹配的最后一个子串的开始位置。这个位置从原始串尾端或fromIndex开始计算。
int length()
返回字符串的长度
int codePointCount(int startIndex,int endIndex)
返回startIndex和endIndex-1之间的代码点数量,没有配成对的代用字符将计入代码点
String replace(CharSequence oldString,CharSequence newString)
返回一个新字符串,这个字符串用newString代替原始字符串中所有的oldString,可以用String或StringBuilder对象作为CharSequence参数。
String substring(int beginIndex)
String substring(int beginIndex,int endIndex)
返回一个新字符串。这个字符串包含原始字符串中从beginIndex到串尾或endIndex-1的所有代码单元。
String toLowerCase()
String toUpperCase()
返回一个新字符串,这个字符串将原始字符串中的大写字母改为小写,或者将原始字符串中所有小写字母改为大写字母。
String trim()
返回一个新字符串,这个字符串将删除原始字符串头部和尾部的空格。
String join(CharSequence delimiter,CharSequence...elements)
返回一个新字符串,用给定的定界符连接所有元素。
java.lang.StringBuilder
StringBuilder()
构造一个空的字符串构建器
int length()
返回构建器或缓冲器中的代码单元数量
StringBuilder append(String str)
追加一个字符串并返回this
StringBuilder append(char c)
追加一个代码单元并返回this
StringBuilder appendCodePoint(int cp)
追加一个代码点,并将其转换为一个或两个代码单元并返回this。
void setCharAt(int i,char c)
将第i个代码单元设置为c。
StringBuilder insert(int offset,String str)
在offset位置插入一个字符串并返回this。
StringBuilder indert(int offset,Char c)
在offset位置插入一个代码单元并返回this
StringBuilder delete(int startIndex,int endIndex)
删除偏移量从startIndex到-endIndex-1的代码单元并返回this。
String toString()
返回一个与构建器或缓冲器内容相同的字符串。
Java日期与时间
java.util包提供了Date类来封装当前的日期和时间。
Date类提供两个构造函数来实例化Date对象
第一个构造函数使用当前日期和时间来初始化对象:Date()
第二个构造函数接受一个参数,该参数是从1970年1月1日起的毫秒数。Date(long millisec)
Java获取当前日期时间
使用Date对象的toString()方法来打印当前日期和时间


使用SimpleDateFormat格式化日期
SimpleDateFormat是一个以语言环境敏感的方式来格式化和分析日期的类。
SimpleDateFormat允许选择任何自定义日期时间格式。
例子


yyyy是公元年,MM是月份,dd是日期,HH:mm:ss是时分秒
注意
有的格式大写和小写
MM是月份
mm是分
HH是24小时制
hh是12小时制
使用printf格式化日期
printf方法可以很轻松地格式化时间和日期,使用两个字母格式,以%t开头并以下面表格的字母结尾

例子


Java类与对象
面向对象程序设计技术
OOP是一种计算机编程架构
对象是现实世界客观实体,有特定的状态和行为
类是一个模板,描述一类对象的行为和状态;依照显示世界的实体特点,将复杂事物共有的状态和行为抽象封装。
该技术的目标是提高代码的可重用性、可扩充性和程序自动生成,从而提高编程效率。
面向对象的基本特征
封装性
将东西包围起来通过自己想定义的方式获取
把过程和数据包围起来通过定义的接口访问数据。
抽象性
对数据进行实例分析,抽取共有性质的结果。
抽象包括两个方面
过程抽象
数据抽象
继承性
基于层次关系的不同类共享数据和操作的一种机制。
父类定义其所有子类的公共属性和操作,在子类中除了定义自己特有的属性和操作外,还可以对父类中的操作重新定义其实现方法;即重载
多态性
指同一操作作用于不同对象上可以有不同的解释,并产生不同的执行结果。
动态绑定,指在程序运行时才将消息所请求的操作与实现该操作的方法进行连接。
Java和C++面向对象的机制不同点
Java面向对象机制从C++发展而来
完善了C++类的封装、继承、多态和抽象等概念
放弃了C++的多重继承、友元类和运算符重载等易产生歧义且安全性差的特性
采用单重继承+接口的方式实现多重继承功能,提供内存自动管理和异常处理机制。
类
基本概念
类是数据以及对数据的一组操作的封装体
与数据类型相似,类只定义数据及对数据操作的模板,类本身并不真正参与程序运行,实际参与由程序运行的是类的对象
类的构成
成员变量
类中的数据,反映类的状态和特性
成员方法
类中对数据的操作,表示类的行为能力
构造方法
为创建类的实例所使用的
类的成员
类中的成员变量和成员方法的统称
Java的成员修饰符
private
访问权限仅限于类的内部,是一种封装的体现。
例如,大多数成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。
protected
子类可以访问父类使用它修饰的成员,是父类传递给子类的继承信息。
public
表示成员是公开的,所有其他类都可以访问;它具有最大的访问权限,是对象或类对外的一种接口的形式。
无修饰词(默认)
表示包访问权限(相当于C++的友元),同一个包内可以访问,访问权限是包级访问权限,比protected大,比public小。
类声明的格式

必须项
关键字class和自定义的类名
可选项
方括号{}中的
<类>、<泛型>、<父类>、<接口>
合法的Java字符
类名标识符
Java预定通常首字母大写
<修饰符>
说明类属性,如public访问权限、abstract抽象类或final最终类
<泛型>
类的参数,带参数的类称为泛型类
成员方法
用来描述对成员变量的操作,不但要声明方法,还有实现方法
成员变量声明格式

修饰符
说明成员属性的关键字
访问权限
public
protected
private
静态成员
static
最终方法
最终变量
final
成员方法声明格式

修饰符
说明成员属性的关键字
访问权限
public
protected
private
静态成员
static
最终方法
最终变量
final
定义时间类
定义时间Time1类

定义主程序Time1Test

this引用
Java类中的每个成员方法都可以使用代词this引用调用该方法的当前对象自己
1.指代对象本身
this
this用于只带调用成员方法的当前对象自身
2.访问本类的成员变量和成员方法
通过this引用当前对象的成员变量,调用当前对象的成员方法。
this.成员变量
this.成员方法([参数列表])
this引用的例子

一个成员方法中,若没有与成员变量同名的局部变量或形式参数,则this引用可以省略。
当局部变量或形式参数与成员变量同名时,方法体中默认的是局部变量或形式参数,因要访问成员变量必须使用this。
instanceof对象运算符
对象运算符instanceof判断一个对象是否属于指定类及其子类,返回boolean类型。

使用this访问当前类的成员


类的成员方法的参数
形参为普通简单量时,采用传值的方式
Java参数传值
适用范围
8种基本数据类型
boolean
byte
char
short
int
long
float
double
String对象
特点
在内存中复制一份数据,把复制后的数据传递到方法内部
作用
在方法内部改变参数的值,外部数据不会跟着发生改变
例子


形参为对象变量时,采用传引用的方式
Java参数传引用
适用范围
数组,除String外的其他类型的对象,StringBuffer动态字符串是传引用
特点
将对象的地址传递到方法内部
作用
在方法内部修改对象的内容,外部数据也会跟着发生改变
例子


类的构造方法
用于创建类的实例,并对实例的成员变量进行初始化。
构造方法与类同名;构造方法通过new运算符调用。
一个类可以声明多个构造方法对成员变量进行不同需求的初始化。
如果类没有定义构造方法,则编译系统会自动插入一个无参数的默认构造器,该构造器不执行任何代码。
构造方法不需要写返回值类型,也不能定义为void,在方法名前面不声明方法类型。
构造方法可以重载,以参数的个数、类型、顺序。
构造方法例子



Java类缺省构造方法
Java类中如果没有显示定义构造方法,编译器会隐式提供一个没有参数的缺省构造方法;
如果在类中显示定义了构造方法,则不会提供缺省构造方法。
例子


Java成员方法重载
一个类中成员变量不能同名,但成员变量与成员方法可以同名。
重载
一个类中可以有多个同名的成员方法,前提是参数列表必须不同,称为类的成员方法重载。
作用
重载多个方法为一种功能提供多种实现。
区分
第一是参数的个数不一样,第二是参数的类型不一样。只要这两方面有其中一方面不一样就可以重载构成方法。
注意
如果两个方法的返回值不同,而其他都相同。这个时候并不构成方法的重载,在编译的时候会报错。
例子

Java构造方法重载
Java支持构造方法重载,重载的构造方法提供创建实例时的多种初始化方案,如指定若干参数的构造方法、默认构造方法、拷贝构造方法等。
由于Java不支持会产生歧义的参数默认值,这些构造方法必须重载,参数列表必须不同。
例子

Java拷贝构造函数
拷贝构造函数是特殊的构造函数,其参数是该类对象,称为拷贝构造函数,它将创建的新对象初始化为形式参数的实例值,实现对象复制功能。
Java不提供默认拷贝构造方法。
例子

Java的final关键字
修饰普通变量
如果final修饰基本数据类型的变量(包括String),则其数值一旦在初始化之后便不能更改;
例子

示例


1.Java中的(==)号比较两个对象是否为同一对象;
2.语句1说明Java的相同字符串常量在内存分配时,是共享内容,仅创建唯一对象;
3.语句2说明final修饰常量b,在编译时替换了c=b+2中的b,因此,c视为常量值hello2,和a共享同一对象;
4.语句3说明e=d+2中的d没有final修饰,e编译时不替换d,e在内存中和a是不同的两个对象;
5.a.equals(e)是比较a和e两个字符串对象的值。
修饰成员变量
当用final作用于类的成员变量时,成员变量必须在定义时或者构造方法中进行初始化赋值。
当final成员变量被初始化赋值后,就不能再被赋值。
如果类的所有成员变量都定义为final,类创建的对象可以被视为常对象。
例子


注意
当用final修饰的成员变量在定义时初始化,则就不能再构造函数中初始化。
如果final修饰的成员变量在构造方法中初始化时,就不能再定义时初始化;
当final成员变量没有在定义时初始化,就必须显式提供构造函数初始化。
例子

修饰引用变量
如果final是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
但引用所指向的对象数据成员(属性)是可以改变
例子

修饰成员方法的形参
如果final是形参为基本类型变量,则形参作为局部变量传值,对外部变量的值不产生影响,作为局部变量的形参由于有final限定,因此在成员方法中不能被修改。
如果final是形参为引用类型的对象,该形参就不能再次引用其他对象,但传给该形参的实参所引用的对象的数据成员(属性)是可以改变的。
例子


修饰Java类和成员方法
如果final修饰成员方法,该方法禁止该方法在子类中被覆盖或者重写。
如果final修饰类,表明这个类不能被继承。
static变量
static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
被static修饰的成员变量和成员方法独立于该类的任何对象。它不依赖类特定的实例,被类的所有实例共享。
只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到它。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享一个static变量。
static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的成员方法中使用,但是不能在其他类中通过类名来直接引用。
示例


注意
static成员方法只能访问静态成员变量


构造代码块
构造代码块的作用是给对象进行初始化
对象建立时就运行构造代码块,而且优先于构造函数执行。只有对象建立时,才会运行构造代码块,类不能调用构造代码块,而且构造代码块与构造函数的执行顺序是前者先于后者。
构造代码块和构造函数的区别
构造代码块是给所有的对象进行统一初始化,而构造函数是给对应的对象初始化。
构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。构造代码块中定义的是不同对象共性的初始化内容。
例子


静态代码块
随着类的加载而执行,只执行一次,并优先于主函数。静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的。
静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。
静态代码块中的变量是局部变量,与普通函数中的局部变量没有区别。
一个类中可以有多个静态代码块。
例子


例子


例子


例子


例子
继承中的静态代码块和初始化代码块


当涉及到继承时,按照下列顺序执行
1.执行父类的静态代码块,并初始化父类静态成员变量
2.执行子类的静态代码块,并初始化子类静态成员变量
3.执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
4.执行子类的构造代码块,执行子类的构造函数,并初始化子类普通成员变量
继承与多态
继承
概念
继承提供在已有类的基础上创建新类,使新创建的类自动拥有被继承类的全部成员;是构造可复用软件的有效机制。
父类/超类
被继承类
子类/派生类
继承产生的新类
子类自动拥有父类的全部成员,包括成员变量和方法等
子类可以更改从父类继承来的成员,使成员适应新的需求
子类可以增加自己的成员,使类的功能得到扩充。
子类不能删除父类的成员。
单一继承

多重继承

1.一个类有多个父类->继承关系十分混乱
2.Java中仅仅支持单一继承,同时Java采用Interface(接口)变相实现多重继承而避免父类二义性。
实现继承的语法
在Java中实现继承需要使用到extends关键字

例如

子类继承父类所有功能的例子

子类对象的实例化
1.先调用父类的构造->调用父类构造函数
2.再调用子类构造->调用子类构造函数
3.若子类中没有定义构造方法,则它自动地调用父类无参数的构造方法
4.若子类定义了构造方法,应该在第一条语句的位置调用父类的构造方法,否则系统将在此位置插入一条调用父类无参数构造方法的语句。
5.对于父类含有参数的构造方法,只能在子类的构造方法中利用super显式地调用。
Important
子类对象初始化缺省调用父类构造函数


子类对象初始化显式调用父类构造函数


父类带参数的构造函数必须显式调用


父类有参数和无参的构造函数共存时,可隐式调用无参数构造函数


子类成员方法覆盖父类原型相同的成员方法


子类调用父类的成员方法使用super


若父类成员方法为private,则不能在子类调用

原因:父类中的私有成员对子类是不可见的,只能通过父类public成员函数来访问。
子类可定义父类名字类型限定符都相同的属性


子类重写父类名字、限定符、参数都相同的成员方法


子类重写父类的私有成员方法时,不能用父类引用调用子类成员方法

原因:父类的print()为私有的成员方法
子类重写父类的protected方法时,能用父类引用调用子类的该方法


父类引用不能调用子类新扩展的成员方法,如print()


重要结论
父类对象引用子类实例
如:Person p = new Student()
子类对象不能引用父类实例
如:Student s = new Person()是错误的
如果使用父类对象引用子类实例,父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法。
多态
多态性是指一个程序中同名的不同方法共存的情况,即一个程序中相同名字方法表示不同的实现。
表现
1.表现在继承中->方法的重写
多个子类从同一父类继承而来
每个子类都重写父类的某个方法
被重写的方法在不同的子类中有不同的形式
例:动物会叫,因此人、狗、猫都会叫,但是叫的形式不同。
2.表现在一个类中->方法的重载
一个类有多个同名的方法,但这些方法的参数个数或类型不一样
例:人吃不同的东西采用不同的形式
类型
编译时多态性
对于多个同名方法,若在编译时能够确定执行同名方法中的哪一个,则称为编译时多态性
方法的重载都是编译时多态性
运行时多态性
如果在编译时不能确定、只有在运行时才能确定执行多个同名方法的哪一个,称为运行时多态
方法的覆盖
表现出两种多态性,当对象引用本类实例时,为编译时多态性,否则为运行时多态性。
重载类的成员方法实现多态

子类重写父类成员方法实现多态

当子类从父类继承来的成员不能满足子类需要时,子类不能删除它们,但可以重定义它们,扩充父类成员方法的功能,使父类成员能够适应子类新的需求。
子类重定义父类成员包括
1.重定义父类的成员变量,则隐藏父类的成员变量。
2.重定义父类的成员方法,如果参数列表相同,则覆盖父类的成员方法,否则重载。
子类重定义父类成员表现出多态性,父类对象引用父类成员,子类对象引用子类成员。重定义的同名成员之间不会产生冲突和混乱。
成员方法的重写规则总结
1.参数列表必须完全与被重写方法的相同。
2.返回类型必须完全与被重写方法的返回类型相同。
3.访问权限不能比父类中被重写的方法的访问权限更低。
例如:父类的方法被声明为public,那么在子类中重写该方法就不能声明为protected。
4.父类的成员方法只能被它的子类重写。
5.声明为final的方法不能被重写。
6.声明为static的方法不能被重写,但是能够被再次声明。
7.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
8.子类和父类不在同一个包中,那么子类可以重写父类的声明为public和protected的非final方法。
9.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。
10.构造方法不能被重写。
例子


抽象方法与抽象类
注意
抽象方法只有声明而没有具体的实现,抽象方法必须使用abstract关键字进行修饰
若一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字进行修饰
抽象类中可以有零个或多个抽象方法,也可以包含非抽象的方法。抽象类中可以没有抽象方法,有抽象方法的的类必须是抽象类。
抽象类可以派生子类,在抽象类派生的子类中必须实现抽象类中定义的所有抽象方法。
抽象类不能创建对象,创建对象的工作由抽象类派生的子类来实现
若父类已有同名的abstract方法,则子类中就不能再有同名的抽象方法。
abstract不能与final并列修饰同一个类;不能与private、static、final或native并列修饰同一个方法。
抽象方法与抽象类的作用
抽象类
用于描述抽象的概念,其中声明的方法为多个子类约定方法声明,每个子类可以根据自身的实际情况,给出方法的具体实现,显然不同的子类可以有不同的方法实现。
抽象方法
用于声明方法的参数和返回值,具体实现由抽象类的子类完成,所有的子类必须实现父类的抽象方法。
提供方法声明与方法实现的分离机制,使得抽象类的多个不同的子类能够表现出共同的行为能力。
抽象类实现多态的例子


抽象类和抽象方法的例子


声明最终类final
最终类
使用关键字final声明的类,不能被继承

注意
抽象类不能被声明为最终类
最终方法
使用关键字final声明的成员方法,不能被子类覆盖

注意
最终类中包含的都是最终方法,非最终类也可以包含最终方法
类的接口
概念
1.接口是一个抽象类型,是抽象方法的集合。
2.接口可以实现类间多继承结构。
3.接口内部只能定义只有共有的抽象方法和全局常量,没有构造方法。
4.接口中的所有方法都需要在子类中实现。
接口的定义格式

在定义接口时,若没有修饰符public,则该接口只能由同一个包中的类或接口引用。
接口的实现

例子


接口的继承例子


接口的使用原则
由于接口里面存在抽象方法,,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下:
1.接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口。
2.接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法。
3.若接口子类不实现全部成员方法,则必须将该类定义为abstract抽象类。
4.接口的对象的可以利用子类对象的向上转型进行实例化
接口的例子


接口中默认是public修饰,若子类方法没用public修饰,则是错误的。

接口和抽象类混用的示例


接口的实际应用--制定标准
USB、打印机接口

使用适配器实现接口方法很多,而子类只需少数方法的问题


Java内部类(inner class)
概念
1.内部类提供了更好的封装,只有外部类能访问内部类。
2.内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private。
3.内部类可以独立继承接口,不受外部类是否继承接口影响

内部类可以直接访问外部类的元素,但是外部类不可以直接访问内部类的元素


内部类可以通过OuterClass.this来获得外部类的引用


外部类可以通过内部类引用间接访问内部类元素


成员内部类
成员内部类是最普通的内部类、是外围类的一个成员,可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点
1.成员内部类中不能存在任何static的变量和方法。
2.成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
例子


成员方法中定义内部类---局部内部类
是嵌套在方法和作用于内的,对于这个类的使用主要是应用于解决比较复杂的问题,想创建一个类来辅助外部类,又不希望这个类是公共可用的,所以就产生了局部内部类。
局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
例子


内部类可以没有名字---称为匿名内部类


1.匿名内部类是没有访问修饰符的。
2.new匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。
3.注意:匿名内部类是没有构造方法,因为它没有名字,更没有构造方法。
静态内部类
1.static可以修饰成员变量、方法、代码块,其他还可以修饰内部类,使用static修饰的内部类称之为静态内部类。
2.静态内部类与非静态内部类之间最大的区别是:非静态内部类在编译完成后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。
3.静态内部类的创建是不需要依赖于外围类。
4.静态内部类不能使用任何外围类的非static成员变量和方法。
例子


包与异常处理
包
Java包及作用
包是Java提供的一种区别类的名字空间机制,是类的组织方式,是一组相关类和接口的集合,提供了访问权限和命名的管理机制。
把功能相似或相关的类或接口组织在同一个包中,方便查找和使用。
同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此包可以避免名字冲突。
在Java中,访问权限是以包为单位的。
Java创建包
创建包可以通过在类或接口的源文件中使用package语句实现,格式如下
package 包名;
包名必选,用于指定包的名称,包的名称为合法的Java标识符。当包中还有包时,可以使用“包1.包2....包n”进行指定,其中,包1为最外层的包,而包n为最内层的包。
例子
在user.date包中定义Date类

在user.employee包中定义Employee类

在默认包中定义EmployeeTest类

输出结果

注意
1.java.lang.Object类是所有Java类的最高层次父类。
2.toString是Object类的方法,所有类都从Object类继承来的。
3.如果定义的类没有覆盖toString方法,则对象在调用toString方法时用的是Object类的toString方法。
4.System.out.println(obj)在参数是一个对象时,会首先调用该对象的toString方法。
常用的Java核心包
1.java.lang包:
包含Java语言的基本类与核心类,如String、Math、Integer、System和Runtime,该包中的类是被编译器隐式导入的。
2.java.awt包和javax.swing包
包含一些图形界面(GUI)的类。
3.java.io包
包含一些用于输入输出(I/O)处理的类。
4.java.util包
实用程序包,提供实现不同功能的类。
5.java.net包
Java网络包,提供与网络操作相关的类。
6.java.sql包
包含与数据库编程相关的类/接口。
7.java.applet包
包含一些用于创建Java小应用程序的类。
除了java.lang外,若要使用其他包的类,应在程序开始引入相应的包。
1.import java.io.*;
2.import java.util.Date;
3.import java.awt.*;
异常处理
概念
异常就是程序执行过程中出现的不正常现象。
任何一个程序都可能出现异常,Java使用对象表示对打开的文件不存在、内存不够、数组访问超界等非预期情况。
Java使异常处理标准化,使程序设计思路更清楚,理解更容易。
异常的类型
Error
产生了非常严重的问题。即使可能使程序回复正常也非常困难,如内存不足等。对于这一类问题,一般不要求应用程序进行异常处理。
RuntimeException
表明产生了设计或执行的问题,如果程序设计正确应该能够避免发生这类问题,如在访问数组时,数组下标越界等。对于这类问题一般不要求处理,使该类问题能够暴露出来,从而改正程序。
其它Exception
由于执行环境的影响,不可避免地将产生的问题。如用户敲入错误的文件名而导致文件没有找到等。对于这类问题,Java强烈要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后是程序继续执行。
异常类的层次
Java定义的异常类有分类层次。所有异常类都是Throwable类的子类。
Throwable属于java.lang包,在程序中不必使用import语句引入。
Throwable类有三个最基本的子类
Error类
Exception类
RuntimeException类
Throwable
Error
VirtualMachError
StackOverFlowError
OutOfMemoryError
AWTError
Exception:异常层次结构的根类
IOException:I/O异常的根类
EOFException
文件结束
FileNotFoundException
找不到文件
InterruptedException
线程中断
RuntimeException:许多java.lang异常的基类
ArithmeticException
算术错误情形,如以0作除数
MissingResourceException
ClassNotFoundException
不能加载所需的类
NumberFormatException
数字转化格式异常,比如字符串到float型数字的转换无效
NullPointerException
尝试访问null对象成员
IllegalArgumentException
方法接收到非法参数
ArrayIndexOutOfBoundException
数组大小小于或大于实际的数组大小
UnknownTypeException
Throwable类常用的方法
getMessage()
获取更详细的异常信息,但并非每个异常都有详细信息。如果没有详细信息,该方法调用后返回空值。
toString()
获取异常的简短描述
printStackTrace()
打印异常发生处堆栈跟踪的信息,包括异常的类名、方法名及所在程序的行数。
发生异常时,就会抛出一个异常,通过捕获这个异常,可以进行相应异常处理。

注意
如果在catch所要捕获异常类存在继承关系时,则必须将子类放在超类的前面,否则子类异常被抛出时永不会被子类捕获,因为在前的超类就已经匹配。
异常类1是异常类2的子类,则其捕获异常的顺序为:

例子


嵌套的异常处理
在try-catch-finally结构中,可以使用嵌套形式,即在捕获异常处理过程中,可以继续抛出异常。
在这种嵌套结构中,产生异常后,首先与最内层的try-catch-finally结构中的catch语句进行匹配比较。
如果没有相匹配的catch语句,该异常情况可以被抛出,让外层的try-catch-finally的结构重复进行匹配检查。这样从最内层到最外层,逐一检查匹配,直到找到一个匹配为止。
例子


throw语句
概念
throw语句用于可能产生应用程序的特定程序,这时应用程序应该给用户提供明确的指示,帮助用户正确理解和使用该应用程序。
格式


抛出异常的方法并不处理该异常,而是由调用该方法的另一方法来处理,那么这时可以使用throw语句给方法声明一个例外情况,其声明格式为:

例子

例子


自定义异常类
用户可以根据需要定义异常类。则要完成三件事
1.生成Throwable类或其子类的一个子类。
2.在可能发生异常的地方,判断是否发生异常,如果发生异常,则throw抛出异常。
3.用try-catch-finally结构来捕获异常,进行处理。
例如

例子


输入输出流
基本概念
流是指在计算机的输入与输出之前运动的数据序列。
流序列中的数据既可以是未经加工的原始的二进制数据,也可以是经一定编码处理后复合某种格式规定的特定数据。
流的输入/输出系统与物理设备链接。尽管与它们链接的物理设备不尽相同,所有流的行为具有相同方式。

Java中的流
Java中,把不同类型的输入、输出源抽象为流,而其中输入或输出的数据则称为数据流,用统一的方式表示。
流的方向是重要的,根据流的方向,流可以分为两类:输入流和输出流。
输入流只能从中读取数据,而不能向其写出数据,而不能从中读取数据。
流的源端和目的端可简单地看成是字节的生产者和消费者
对输入流,可不必关心它的源端是什么,只要简单地从流中读取数据。
对输出流,也可不知道它的目的端,只是简单地往流中写数据。
流的分类
按流的方向分:
输入流
输出流
读写操作的单位分:
字节流
对数据以字节为单位进行读写,既可以一个字节一个字节的读写数据,也可以一次读写的是任意长度的字节块(即字节数组)。
字符流
在读写流内数据时是以字符为单位,一次一个字符,或者一次一个字符块。
逻辑上分:
节点流
指直接从指定的位置读或写,其他的流则称为过滤器。
过滤器
输入流往往是以其他输入流作为它的输出源,经过过滤或处理后再以新的输入流的形式提供给用户,过滤器输出流的原理也类似。
Java.io包中封装类层次
Object
File
file类
操作系统的文件管理是向应用程序提供的最基本服务之一,但在计算机的发展过程中,形成了众多的文件管理系统,它们互不兼容,给用户编程留下了很大得困难,Java消除了这种不兼容性。
file类能够处理由本地文件系统维护的具体we年,它提供独立于平台的文件处理方法。
file类的构造函数
public File(String path);
public File(String path,String name);
public File(File dir,String name);
例子


file类创建与删除文件或目录
boolean createNewFile()
不存在返回true,存在返回false
boolean mkdir()
创建目录
boolean mkdirs()
创建多级目录
boolean delete()
用于删除文件或删除空目录
boolean deleteOnExit()
文件使用完成后删除
file类创建删除的例子


file类查询文件状态与属性的方法
boolean canExecute()
判断文件是否可执行
boolean canRead()
判断文件是否可读
boolean canWrite()
判断文件是否可写
boolean exists()
判断文件是否存在
boolean isDirectory()
判断是否是目录
boolean isFile()
判断是否是文件
boolean isHidden()
判断是否是隐藏文件
boolean isAbsolute()
判断是否是绝对路径
Java只能查询上述文件属性,其他属性不能查询,如隐藏、系统、归档等属性则无法查询。File类的平台独立性,以放弃某些系统的个性为代价。
file类文件目录管理的方法
String getName()
返回File对象名
String getPath()
返回File对象的路径
String getAbsolutePath()
返回File对象的绝对路径
String getParent()
如果没有父目录返回null
long lastModified()
获取最后一次修改的时间
long length()
返回File对象的长度
boolean renameTo(File f)
文件改名
File[] listRoots()
获取机器盘符
String[] list()
列出目录文件
String[] list(FilenameFilter filter)
列出目录中满足要求的文件
注意
File类不允许访问文件内容,即读写文件;也不能改变文件的属性。
列出磁盘下文件或文件夹


利用递归列出全部文件


RandomAccessFile
RandomAccessFile类
File类具有查询文件属性、状态和文件名等功能,但不能访问文件内容。
在java.io包中RandomAccessFile类和输入输出流类具有读写文件的功能。
RandomAccessFile类只能进行文件的输入输出,而流类功能更加强大,它可以进行包括文件的一切输入输出操作。
RandomAccessFile类的构造函数


其中使用参数的含义是
name是一个String对象,表示被访问的文件名。
file是一个File对象,表示被访问的文件名,用这种方式提供文件名,应用程序独立于平台。
mode用字符串表示被访问文件的读写模式:
“r”表示文件以只读方式打开。
“rw”表示文件以读写方式打开。
生成RandomAccessFile对象
以读写方式生成RandomAccessFile对象时
如果该文件不存在,则创建该文件,供程序进行读写操作。
如果该文件已经存在,则以覆盖方式(不是改写)把输出数据写入到文件中,原文件中没有被覆盖的部分,仍然保留在文件中。
在RandomAccessFile类中没有专门打开文件的方法,在生成RandomAccessFile对象时,文件即被打开,可供程序访问,这是文件读写指针为0,即读写位于文件的开头。
RandomAccessFile的成员方法
void close()
关闭此随机访问文件流并释放与该流关联的所有系统资源。
FileChannel getChannel()
返回与此文件关联的唯一FileChannel对象。
FileDescriptor getFD()
返回与此流关联的不透明文件描述符对象
long getFilePointer()
返回此文件中的当前偏移量
long length()
返回此文件的长度
int read()
从此文件读取一个数据字节
int read(byte[] b)
将最多b.length个数据字节从此文件读入数组b
int read(byte[] b,int off,int len)
将最多len个数据字节从此文件读入数组b
boolean readBoolean()
从此文件读取一个boolean
byte readByte()
从此文件读取一个有符号的把位置
char readChar()
从此文件读取一个字符
double readDouble()
从此文件读取一个double
float readFloat()
从此文件读取一个float
void readFully(byte[] b)
从文件当前位置读b.length个字节到数组b
void readFully(byte[] b,int off,int len)
从位置off读到len个字节到数组b
int readInt()
从此文件读取一个有符号的32位整数
String readLine()
从此文件读取文本的下一行
long readLong()
从此文件读取一个有符号的64位整数
short readShort()
从此文件读取一个有符号的16位整数
int readUnsignedByte()
从此文件读取一个无符号的八位数
int readUnsignedShort()
从此文件读取一个无符号的16位数
String readUTF()
从此文件读取一个字符串
void seek(long pos)
设置文件指针到pos偏移量位置
void setLength(long newLength)
设置此文件的长度
int skipBytes(int n)
文件指针跳过n个字符
void write(byte[] b)
将b.length个字节从数组b写入到文件
void write(byte[] b,int off,int len)
从数组b偏移off的len个字节写入文件
void write(int b)
向此文件写入指定的字节
void writeBoolean(boolean v)
按单字节值将boolean写入该文件
void writeByte(int v)
按单字节值将byte写入该文件
void writeBytes(String s)
按字节序列将该字符串写入该文件
void writeChars(int v)
按双字节值将char写入该文件,先写高字节
void writeChars(String s)
按字符序列将一个字符串写入该文件
void writeDouble(double v)
使用Double类中的doubleToLongBits方法将双精度参数转换为一个long,然后按八字节数量将该long值写入该文件,先写高字节
void writeFloat(float v)
使用Float类中的floatToIntBits方法将浮点参数转换为一个int,然后按四字节数量将该int值写入该文件,先写高字节。
void writeInt(int v)
按四个字节将int写入该文件,先写高字节
void writeLong(long v)
按八个字节将long写入该文件,先写高字节
void writeShort(int v)
按两个字节将short写入该文件,先写高字节
void writeUTF(String str)
使用modified UTF-8编码以与机器无关的方式将一个字符串写入该文件。
RandomAccessFile写文件

RandomAccessFile读文件


InputStream
OutputStream
字节流
字节流有两个类层次结构定义。在顶层有两个抽象类
InputStream
有关读入数据的方法
int read()
方法返回一个0--255之间的整数或-1,-1代表遇到了流的结束,其它对应读入的字节。
intread(byte[])
方法是将字节读入参数给定的字节数组,返回值是实际读入的字节数或-1(遇到了流结束)
int read(byte[],int,int)
方法的后两个参数分别给出读入的起始位置和读入的最大字节数。
void close()
关闭当前的流对象,并释放该流对象占用的资源。
int available()
返回当前流对象中还没有被读取的字节数量。也就是获得流中数据的长度。
long skip(long)
跳过当前流对象中的n个字节,而实际跳过的字节数量则以返回值的方式返回。
InputStream类的子类
所有InputStream的子类都是针对不同的输入数据源,其类名的前缀清楚地表示出输入数据源,FileInputStream类的数据源是文件,PipedInputStream类的数据源是管道等等。
FilterInputStream类及其子类是增强的流,它的功能更加强大,使用更加灵活。
java.io包中InputStream的类层次
InputStream
FileInputStream
ByteArrayInputStream
FilterInputStream
DataInputStream
BufferedInputStream
LineNumberInputStream
PushbackInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream
OutputStream
OutputStream类有关读入数据的方法
int write(int)
向流的末尾写入一个字节的数据。
int write(byte[])
将数组b中的数据依次写入当前的流对象
int write(byte[],int,int)
将数组中从开始下标(包含),后续长度的数据依次写入到流对象中
void close()
关闭当前流对象,并释放该流对象占用的资源
void flush()
将当前流对象中的缓冲数据强制输出出去,使用该方法可以实现立即输出。
OutputStream类的子类
所有OutputStream的子类与InputStream的子类相类似,针对不同的输出数据源,其类名的前缀清楚地表示出输出数据源,如FileOutputStream类的数据源是文件,PipedOutputStream类的数据源是管道。
FilterOutputStream类及其子类是增强的流,它的功能更加强大,使用更加灵活。
java.io包中OutputStream的类层次
OutputStream
FileOutputStream
ByteArrayOutputStream
FilterOutputStream
DataOutputStream
BufferedOutputStream
PrintStream
ObjectOutputStream
PipedOutputStream
每个抽象类都有多个具体的子类,这些子类对不同的外设进行处理。
1.抽象类InputStream和OutputStream定义了实现其他流类的关键方法。
2.最重要的两种方法是read()和write(),他们分别对数据以字节为单位进行读写。
3.两种方法都在InputStream和OutputStream中被定义为抽象方法,他们被派生的流类重载。
FileInputStream类
这个类属于节点流,分别完成对文件的输入输出(读写)操作
FileInputStream类的构造器有:
FileInputStream(String)
参数String对象表示文件名
FileInputStream(File)
参数File对象表示文件名
FileInputStream(FileDescriptor)
参数FileDescriptor定义一个本地文件系统对象表示的文件名。
显示文件内容的例子

FileOutputStream类
FileOutputStream类和FileInputStream类在构造器和方法的参数定义上十分相似,只有一个针对输入,另一个针对输出。
FileOutputStream类的构造器有:
FileOutputStream(String)
FileOutputStream(String,boolean)
FileOutputStream(File)
FileOutputStream(FileDescriptor)
生成FileOutputStream对象时,如果文件不存在,则创建该文件供程序输出数据。
如果文件已经存在,则有改写和附加两种输出数据的方式
改写的含义是先把原文件长度截为零,原文件数据被丢弃,然后再输出数据。(第二个构造器的boolean参数为false值和其它构造器的对象)
附加的含义是在原文件末尾追加输出数据,原文件数据仍然存在。(第二个构造器的boolean参数为true)
写文件的例子

DataInputStream和DataOutputStream类
这两个类创建对象分别被称为数据输入流和数据输出流
分别实现了DataInput接口和DataOutput接口
允许程序按与机器无关的风格读写Java数据。
这两个流也是过滤器流,常以其他流如InputSTream或OutputStream作为他们的输入或输出。
他们的输入和输出几乎是对应的,每种基本数据类型的读写方法可以从其后缀名字识别。
readInt()
readBoolean()
readChar()
readDouble()
writeInt()
writeBoolean()
writeChar()
writeDouble()
Reader
Writer
字符流
主要是用来处理字符的。他们在读写流内数据时是以字符为单位。
字符流类由两个类层次结构定义。顶层有两个抽象类:Reader和Writer。这些抽象类处理同一编码的字符流。
抽象类Reader和Writer定义了实现其他流类的关键方法。其中两个最重要的是read()和write(),它们分别进行字符数据的读和写。这些方法被派生流类重载。
Reader及Writer类和它们子类的方法,与InputStream及OutputStream类及它们子类的使用方法非常类似。
java.io包中Reader的类层次
Reader
BufferedReader
LineNumberReader
CharArrayReader
FilterReader
PushbackReader
InputStreamReader
FileReader
PipedReader
StringReader
java.io包中Writer的类层次
Writer
BufferedWriter
CharArrayWriter
FilterWriter
OutputStreamWriter
FileWriter
PipedWriter
PrintWriter
StringWriter
InputStreamReader类和OutputStreamWriter类
在构造这两个类对应的流时,会自动进行转换,将平台缺省的编码集编码的字节转换为Unicode字符。
BufferedReader类和BufferedWriter类
这两个类对应的流使用了缓冲,能大大提高输入输出效率,这两个也是过滤器流,常用来对InputStreamReader和OutputStreamWriter进行处理。
字节流和字符流例子

PiperInputStream和PipedOutputStream管道输入输出流
管道是UNIX的发明,大大增强了流的概念。
管道提供一种线程之间的通信方法,可用于IPC(进程间通信)或是ITC(线程间通信)
输入管道是用来接收输出管道所写的数据。
这两个类必须同时使用,所以他们除了不带参数的构造器外,互为构造器中的参数。
PipedInputStream(PipedOutputStream)
PipedOutputStream(PipedInputStream)
I/O流连接及处理流
Input Stream Chain

Output Stream Chain

字符流与字节处理流的关系对照表

对象序列化
对象序列化处理
对象序列化是Java的一种特性,类实现Serializable接口。
实现Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。
网络传输时进行序列化,能自动弥补操作系统间的差异。
可以实现"优先持久化",意味着对象的“生存时间”取决于程序的每一次调用之间。
序列化一个对象
首先要创建某些OutputStream对象,然后将其封装到ObjectOutputStream对象内。
再需调用writeObject()即可完成对象的序列化,并将其发送给OutputStream
相反将一个InputStream封装到ObjectInputStream内,然后调用readObject()。
最后获得的是一个Object对象的引用,以便能够直接设置。
例子
实现序列化接口的学生类

实现学生类实例的序列化与反序列化

GUI图形界面编程
用户界面概述
用户界面的类型
字符用户界面(CUI):例如MS-DOS
图形用户界面(GUI):Microsoft Windows
GUI的组件
组件
基本的控制组件,例如Button、Label等
容器:能容纳和排列组件的组件
组件继承它的父容器的性质

AWT
AWT是在Swing出现之前用来设计界面的工具包
用awt包中的类创建的用户界面在不同的操作平台上有不同的表现。使用不灵活。
import java.awt.*;
Swing
Swing是对AWT的扩展,它是Java2中的一个标准包。
组件都是以J开头的,例如JFrame,JButton等。swing界面在不同的平台上外观完全一样,真正做到平台独立。
import javax.swing.*;
与图形用户界面有关的包
java.awt
与绘图相关的类
java.event
与事件处理相关的类
javax.swing
与Swing组件相关的类。
AWT抽象窗口工具包
AWT的API为Java程序提供的建立图形用户界面GUI工具集,AWT可用于Java的applet和applications中,其主要功能包括:
1.用户界面组件
2.事件处理模型
3.图形和图像工具,包括形状、颜色和字体类。
4.布局管理器,可以进行灵活的窗口布局而与特定窗口的尺寸和屏幕分辨率无关。
java.awt包中的类
java.awt主要包括
组件
Component
定义了所有的图形化界面元素及绘制方法
容器
Container
可以包含多个基本组件,以便统一操作和管理
布局管理器
LayoutManager
定义了窗口中组件布局排列的方法
AWT类间的关系
组件类、容器类、布局管理器类构成了AWT中的简单基础关系
容器中可以包含组件
所有的容器都包含布局管理器,用来指定容器中组件的位置和形状
图形类提供在组件中显示文本和图形的方法
AWT中许多操作都发生在组件、容器和布局管理器之间
AWT组件类体系结构
Component
Button
Checkbox
Container
ScrollPane
Panel
Applet
Window
Frame
Dialog
Choice
Canvas
TextComponent
TextArea
TextField
Label
AWT组件类
组件是图形用户界面的基本组成元素,范式能以图形化方式显示在屏幕上,并能够与用户进行交互的对象都是组件。
如:菜单、按钮、标签、文本框、滚动条等。
组件不能独立显示出来,必须将组件放在一定的容器中才可以显示出来。
组件是构成AWT的基础
AWT中包括大量组件,其中多数类是由java.awt.Component类扩展而来的。
java.awt.Component是一个抽象类,是除菜单相关组件之外所有AWT组件类的根父类,Component类中定义了大量的属性和放阿飞,这些属性和方法规定了GUI组件的基本特性,如尺寸、位置、颜色、字体等,这些方法也实现了作为一个GUI部件所应具备的基本功能。
同时,也为其派生类提供了许多功能。(继承)
AWT组件类的方法
类java.awt.Component是许多组件类的父类,封装了组件通用的方法和属性,如图形的组件对象大小、显示位置、前景色和背景色、边界、可见性等,许多组件类也继承了Component类的成员方法和成员变量,相应的成员方法包括:
getComponentA(int x,int y)
getFont()
getForeground()
getName()
getSize()
paint(Graphics g)
repaint()
update()
setVisible(boolean b)
setSize(Dimension d)
setName(String name)等
AWT容器类
容器java.awt.Container是Component的子类,一个容器可以容纳多个组件,并使他们成为一个整体。
容器可简化图形化界面的设计,以整体结构来布置界面;所有的容器都可以通过add()方法向容器中添加组件。
有三种类型的容器:
Window
Panel
ScrollPane
AWT中有两种主要的容器类型:
java.awt.window
java.awt.panel
从Container类派生的AWT常用容器
Applet
Panel.Applet的扩展,使用所有Applet的超类
Dialog
对话框(可模式化或非模式化Window的扩展)
FieldDialog
用于选择文件的对话框
Frame
Window的扩展,应用程序的容器,可以有菜单和标题栏。
Panel
Container的扩展,简单的容器
ScrollPane
滚动组件
Window
Container的扩展,没有菜单或边界,是Frame和Dialog的超类
Frame容器
通常是有哪个Window的子类Frame来实例化生成一个窗口,而不是直接用到Window类。
Frame的外观就像在Windows系统下见到的窗口、有标题、边框、菜单、大小等。每个Frame的对象实例哈局以后,都是没有大小和不可见的,因此必须调用setSize()来设置大小,调用setVisible(true)来设置该窗口为可见的。
AWT在实际的运行过程中是调用所在平台的图形系统,因此同样一段AWT程序在不同的操作系统平台下运行所看到的图形系统是不一样的。
例如在WIndows下运行,则显示的窗口是windows风格的窗口;而在UNIX下运行时,则显示的是UNIX风格的窗口。
例子


Panel类
Panel是一个java.awt.Panel的对象。
Panel包含在另一个容器中,或是在Web浏览器的窗口中。
Panel确定一个四边形,其他组件可以放入其中。
Panel必须放在Window或Window的子类中,以便能显示出来。
为组件提供空间,允许子面板有自己的布局管理器
以add方法添加组件
当创建面板对象时,为了能看得见,必须使用add方法添加到窗口活框架对象上,并调用setVisible(true)来设置该窗口为可见。
例子


布局管理器
java为了实现跨平台的特性并且获得动态的布局效果,java将容器内所有组件安排给一个布局管理器负责管理。
组件排列顺序,组件的大小、位置,当窗口移动或调整大小后组件如何变化等功能,授权给对应的容器布局管理器来管理,不同的布局管理器使用不同的算法和策略,容器可以通过选择不同的布局管理器来决定布局。
Java中的布局类型包括以下几种:
FlowLayout
流式布局
流式布局管理器
FlowLayout是Panel,Applet的缺省布局管理器。其组件的放置规律是从上到下、从左到右进行放置,如果容器足够宽,第一个组件先添加到容器中第一行的最左边,后续的组件依次添加到上一个组件的右边,如果当前行已放置不下该组件,则防止到下一行的最左边。
构造方法主要是以下几种:
FlowLayout(FlowLayout.RIGHT,20,40);
第一个参数表示组件的对齐方式,指组件在这一行中的位置是居中对齐、居右对齐还是居左对齐。
第二个参数是组件之间的横向间隔。
第三个参数是组件之间的纵向间隔,单位是像素。
FlowLayout(FlowLayout.LEFT);
居左对齐,横向间隔和纵向间隔都是缺省值5个像素。
FlowLayout();
缺省的对齐方式为居中对齐,横向间隔和纵向间隔都是缺省5个像素。
例子


FlowLayout局管理器的特点:
当容器的代销发生改变时,用FlowLayout管理的组件会发生变化,其变化规律是:组件的大小不变,但是相对位置会发生变化。
例如:

BorderLayout
边界布局
边界布局管理器
BorderLayout是Window,Frame和Dialog的缺省布局管理器。
BorderLayout布局管理器把容器分成5个区域:
North
South
East
West
Center
每个区域只能方式一个组件。各个区域的位置和大小如下所示:

例子


BorderLayout局管理器的特点
在使用BorderLayout的时候,如果容器的大小发生变化,其变化规律为:组件的相对位置不变,大小发生变化。
例如

North区域缺少组件的运行效果

North和Center区域缺少组件的结果

GridLayout
网格布局
GridLayout局管理器
使容器中各个组件呈网格状布局,平均占据容器的空间。
例子


GridBagLayout
网格包布局
CardLayout
卡片布局
CardLayout局管理器
CardLayout布局管理器能够帮助用户处理两个以至更多的成员共享同一显示空间,它把容器分成许多层,每层的显示空间占据整个容器的大小,但是每层只允许放置一个组件,当然每层都可以利用Panel来实现复杂的用户界面。
CardLayout布局管理器就像一幅整齐的扑克牌。
构造函数
CardLayout()
CardLayout(int hgap,int vgap)
常用方法
void first(Container)
void last(Container)
void next(Container)
void previous(Container)
例子


Java事件处理机制
事件
事件的概念与事件对象
指用户在界面上的一个操作(各种输入设备);当一个事件发生时,该事件用一个事件对象来表示。事件对象有对应的事件类。不同的事件类描述不同类型的用户动作。事件类包含在java.awt.event和javax.swing.event包中。
事件源
产生事件的组件叫事件源。在按钮上单击鼠标时,该按钮就是事件源,会产生一个ActionEvent类型的事件。
事件类型
事件处理机制
事件处理器(事件处理方法)
是一个接受事件对象并进行相应处理的方法。事件处理器包含在一个类中,这个类的对象负责事件是否发生,若发生就激活事件处理器进行处理。
事件监听器接口与事件监听器类
事件监听器类
包含事件处理器,并负责检查事件是否发生,若发生就激活时间处理器进行处理的类叫做时间监听器类。其实例就是事件监听器对象。事件监听器类必须实现时间监听器接口或继承事件监听器适配器类。
事件监听器接口定义了处理事件必须实现的方法。事件监听器适配器类是对事件监听器接口的简单实现。目的是为了减少编程的工作量。
事件监听器接口和事件监听器适配器类也都包含在java.awt.event和javax.swing.event包中。
注册事件监听器
为了能够让事件监听器检查某个组件(事件源)是否发生了某些事件,并且在发生时激活事件处理器进行相应的处理,必须在事件源上注册事件监听器。这是通过使用事件源组件的以下方法来完成的:
addXxxListener(事件监听器对象),Xxx对应相应的事件类。
事件监听器适配器
委托事件处理

事件处理实例:关闭窗口

GUI设计
GUI的设计步骤
建立用户界面
1.设计一个顶层容器对象,如Frame
2.确定布局,增加组件
3.改变组件颜色、字体
增加事件处理
1.编写事件监听器类(内含事件处理方法)
2.在事件源上注册事件监听器对象
显示用户界面
AWT事件类的继承体系
EventObject
AWTObject
ActionEvent
AdjustmentEvent
ComponentEvent
ContainerEvent
ForcusEvent
InputEvent
KeyEvent
MouseEvent
PaintEvent
WindowEvent
ItemEvent
TextEvent
事件类
低级事件
ComponentEvent
组件事件:组件尺寸的变化,移动
ContainerEvent
容器事件:组件增加,移动
WindowEvent
窗口事件:关闭窗口,窗口闭合,图标化
FocusEvent
焦点事件:焦点的获取和丢失
KeyEvent
键盘事件:键按下、释放
MouseEvent
鼠标事件:鼠标单击、移动
高级事件(语义事件)
ActionEvent
动作事件:按钮按下,TextField中按Enter键
AdjustmentEvent
调节事件:在滚动条上移动滑块以调节数值
ItemEvent
项目事件:选择项目,不选择“项目改变”
TextEvent
文本事件:文本对象改变
编写事件处理程序

常用监听器
常用监听器1:ActionListener
ActionListener可能是使用最多的监听器,与之对应的事件类型是ActionEvent,一般在鼠标点击某个按钮时会产生该事件。
该接口只包含有一个抽象方法,其原型如下:

其实现类必须重写actionPerformed方法,当事件发生时将调用该方法。
常用监听器2:KeyListener
KeyListener专门用来处理键盘事件,其对应事件类型是KeyEvent
该接口中包含有三个抽象方法,分别在不同的时刻被调用,原型如下

常用监听器3:MouseListener
操作鼠标时会产生鼠标事件MouseEvent,而MouseListener用来处理鼠标的操作,其原型为

常用监听器4:MouseMotionListener
MouseMotionListener是专门用来处理鼠标运动事件的,比如将鼠标进行移动和拖动的时候,该接口的原型如下:

常用监听器5:ItemListener
当下拉列表、单选按钮、复选按钮的选项发生改变时,都会产生选项事件ItemEvent,如果需要处理这样的事件,就用到了ItemListener,其原型:

常用监听器6:WindowListener
操作窗口时会产生窗口事件WindowEvent,其对应监听器是WindowListener,原型如下:

常用监听器7:FocusListener
某个组件得到/丢失焦点时将产生焦点事件FocusEvent,可以使用FocusListener来处理这样的事件,该接口原型:

常用监听器8:AdjustmentListener
移动滚动条等组件产生AdjustmentEvent,可以使用AdjustmentListener来处理这样的事件,该接口原型为:

常用监听器9:TextListener
文本字段或文本区发生改变产生TextEvent,可以使用TextListener来处理这样的事件,该接口原型:

常用监听器10:ComponentListener
对象移动缩放显示隐藏等产生ComponentEvent,可以使用ComponentListener来处理这样的事件,该接口原型为:

常用监听器11:ContainerListener
容器中增加删除了组件ContainerEvent,可以使用ContainerListener来处理这样的事件,该接口原型:

事件源与监听器的关系
一个事件源可以对应多个监听器。
一个监听器可以对应多个事件源。

Java事件监听器的四种实现方式
1.自身类作为事件监听器
用户定义监听器派生类实现监听器接口
例如


2.外部类作为事件监听器
用户定义内部类实现监听器接口
例子


3.匿名内部类作为事件监听器
用户用匿名类实现监听器接口
例子


4.内部类作为事件监听器
用户定义内部类实现监听器接口
例子


事件适配器(Adapter)
要实现相应的事件监听器接口,就必须实现其中的所有方法;有的接口中包含多个事件处理方法,而有时只需要其中少数几个,其他方法只是空实现。
为简化编程,JDK针对大多数事件监听器接口定义了相应的实现类,这种实现类称之为事件适配类。
在适配器类中,实现了相应监听器接口的所有方法,但不做任何事情,即这些Adapter类中的方法都是空的。
只要继承适配器类,就等于实现了相应的监听器接口。
如果要对某类事件的某种情况进行处理,只要覆盖相应的方法,其中的方法不用实现,所以定义的Listener可以继承Adapter类,而且只需重写所需要的的方法。
事件适配器(Adapter)类型
ComponentAdapter
组件适配器
ContainerAdapter
容器适配器
FocusAdapter
焦点适配器
KeyAdapter
键盘适配器
MouseAdapter
鼠标适配器
MouseMotionAdapter
鼠标运动适配器
WindowAdapter
窗口适配器
例子

Swing高级图形库
awt:Abstract Window Toolkit--抽象窗口工具集
awt处理用户界面元素的方法是把这些元素的创建和行为委托给每个目标平台上的本地GUI处理。
1.每个平台提供的图形界面元素都不一样
2.不同平台上的awt库存在不同的BUG
awt的事件处理模型在Java1.1版进行大的改动后,到目前的版本基本没变。
尽管awt的用户界面组件仍然可以用,但是建议最好不要使用。
swing
Swing组件全部都是由纯Java编写的,用户界面元素都绘制在空白窗口上,绘制和行为都由swing类自己完成。各平台之间唯一不同的就是最外层窗口的创建。
现在编写Java图形界面程序,使用swing组件+awt事件处理模型。
Swing常用组件类的体系结构
java.lang.Object
java.awt.Component
java.awt.Component
java.awt.Panel
java.applet.Applet
JApplet
java.awt.Window
java.awt.Dialog
JDialog
JWindow
java.awt.Frame
JFrame
javax.swing.JComponent
Awing组件
实战演练
创建一个框架
Java中,顶层窗口称作框架
swing中实现框架功能的类是JFrame
swing组件类的名称一般以J开头,以区别于原awt组件
这个窗口是由操作系统绘制和管理的。
javax开头的包在Java1.1中是扩展包,在2.0中已经是核心包的一部分。


改变JFrame外观的常用方法
改变框架的外观,要使用JFrame的方法


添加菜单
为框架添加菜单需要以下步骤
创建一个JMenuBar对象,用Frame的setJMenuBar方法设置为框架菜单。
创建JMenu对象,添加到JMenuBar对象中。
创建JMenuItem对象,添加到JMenu对象中。

程序代码


高级菜单功能
为菜单项添加图标
JMenuItem(String,Icon)
newItem.setIcon(new ImageIcon("graph.jpg"));
ImageIcon类表示图标对象
复选框和单选按钮菜单
JRadioButtonMenuItem
JMenuItem saveItem=new JRadioButtonMenuItem("Save");
JCheckBoxMenuItem
editMenu.add(new JCheckBoxMenuItem("Cut");
多级菜单
JMenu.add(JMenu)
editMenu.add(optionMenu);
菜单项的启用和禁用
JMenuItem.setEnable(Boolean)
pasteItem.setEnabled(false);
菜单项的删除
JMenu.remove(int)
optionMenu.remove(1);
程序代码


向窗口添加组件
组件要添加到窗口的内容容器中
例子


安排组件位置
Java中有两种方法安排组件在窗口中的位置
使用组件在窗口中的绝对坐标和大小
无法根据窗口大小自动排列窗口元素
使用Java提供的布局管理器
例子


组件大小
Java中对组件大小的管理比较复杂,主要是为了满足布局管理器自动布局的需要。
每个组件有四个与大小有关的属性
Size
组件的当前实际大小
MaximumSize
组件的最大尺寸
MinimumSize
组件的最小尺寸
PreferredSize
组件的最佳尺寸,缺省值和组件内容有关
内容说明
不同的布局方式采用不同的属性来计算,设置Size不一定起作用
若不使用布局管理器,则只有Size起作用
各属性可以通过get和set方法来读取或设置

Swing组件
程序与用户的交互,通过窗口中的组件来完成
窗口中的一个组件就是一个对象
Swing包中常用GUI组件包括
JButton
按钮
JCheckBox
可选框
JComboBox
组合框
JLabel
标签
JList
列表
JPasswordFoeld
密码域
JProgressBar
进度条
JRadioButton
单选按钮
JScrollBar
滚动条
JTable
表格
JTextArea
文本区
JTextField
文本域
JTree
树形框
例子

各组件的常用方法
各组件的外观和内容都是由组件的参数决定的,如按钮上的文字等。设置和读取组件的普通参数:
改变各组件的参数使用
set*****(***)方法
读取各组件的参数使用
get*****(***)方法
带有列表内容的组件,如组合框,其列表内容一般通过下面方法设置和读取:
添加一项
addItem(***)方法
删除一项
removeItem(***)方法
Swing的布局管理器
布局管理器是一个对象
当容器大小变化时,此对象将自动调整容器中个组件的大小和位置
目的是使各组件在不同平台上排列和显示都正常。
可以为Container类型的对象设置各种布局管理器
使用Container的方法

setLayout(null)表示不使用布局管理器
布局管理器包括以下几种
FlowLayout
流式布局管理器
流式布局管理器如下安排容器中的组件:
将各组件设置为最合适的大小(由组件内容决定或通过setPreferredSize设置)
按顺序在一行上水平排列组件,直到没有足够剩余空间,另起一行。
可以决定组件在一行上是左对齐、居中、右对齐、或两边对齐。
可以决定组件的水平间距和垂直间距。
例子


BorderLayout
边界布局管理器
边界布局管理器如下安排容器中的组件:
将容器划分为5个区域,每个区域至多只能放置一个组件。
南北两个组件的宽度随容器宽度变化,高度采用最佳大小。
东西两个组件的高度随容器高度变化,宽度采用最佳大小。
中间组件占满剩余空间。
可以决定组件的水平间距和垂直间距。

例子


CardLayout
分页布局管理器
GridLayout
网格布局管理器
网格布局管理器如下安排容器中的组件
将容器平均划分为一个m行n列的表格,各方格大小相同。
组件按加入的顺序依次放入第一行第一格,第一行第二格,,,,
可以决定网格的水平间距和垂直间距。
例子


GridBagLayout
网格组布局管理器
网格组布局管理器如下安排容器中的组件
将容器平均划分为一个m行n列的表格,各方法大小相同。
一个组件可以占用多个方格组成的矩形区域。
例子



BoxLayout
箱式布局管理器
箱式布局管理器如下安排容器中的组件
将容器划分为n行/列(横/竖箱子),各行高/列宽度不定。
每个横/竖箱子内可以水平/竖直排列组件。
可以插入三种页数不可见组件:支柱、固定区、胶水

例子


还可以创建自己的布局管理器,只要实现LayoutManager接口
JPanel--面板
面板是组件的小容器,且能作为一个组件被安排在更大的容器中。
面板的实际作用是使得容器可以嵌套容器。
每个面板可以有自己的布局管理器,使得一个窗口中有多种布局方式。

例子


实现特定功能的面板
JScrollPane
实现从一个小窗口观察大组件的功能。
例子


JSplitPane
实现切分窗口功能
例子


JTabbedPane
实现分页显示功能
例子


工具栏
工具栏是提供最常用功能的快捷键按钮栏。
工具栏可以四处移动
例子


对话框
对话框也是一种窗口,与框架的区别是
对话框可以依附于某个框架
模式对话框可以强制用户输入信息或阅读信息
对话框派生子JDialog类

例子


标准对话框
为方便起见,Java提供了一些标准的对话框形式。
类javax.swing.JOptionPane有四个静态方法来显示常用的一些简单的选项对话框:
showMessageDialog
显示一条消息并等待用户点击OK
showConfirmDialog
显示一条消息并等待用户选择OK/Cancel
showOptionDialog
显示一条消息并让用户在一组选项中选择
showInputDialog
显示一条消息并让用户输入一行文本
类javax.swing.JFileChooser实现了文件选择对话框
既可以用于打开文件,也可以用于保存文件
文件选择对话框是模式对话框
类javax.swing.JColorChooser实现了颜色选择对话框
颜色选择对话框可以是模式的,也可以是无模式的。
可以将颜色选择器组件添加到一个容器中。
选项对话框
可以分为四个区域
每一部分内容都可以在方法参数中设置。
设置的方法是使用int数值常量表示
图标

按钮

返回值

返回值通常是int数值常量
例子



文件选择对话框
可以设置下列属性
当前目录
当前文件
文件过滤器
是否支持多选
例子



多线程编程
程序、进程和多任务
概念
程序
程序是对数据描述与操作的代码的集合,是应用程序执行的脚本。
进程
进程是程序的一次执行过程,是操作系统运行程序的基本单位。
程序是静态的,进程是动态的系统。
系统运行一个程序就是一个进程从创建、运行到消亡的过程。
多任务
多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每一个任务对应一个进程。
线程
线程是比进程更小的运行单位,是程序中单个顺序的流控制。一个进程中可以包含多个线程。
线程是一种特殊的多任务方式。当一个程序执行多线程时,可以运行两个或更多的由同一个程序启动的任务。这样,一个程序可以使得多个活动任务同时发生。
线程与任何一个程序一样有一个开始、一系列可执行的命令序列、一个结束。在执行的任何时刻,只有一个执行点。线程与程序不同的是线程本身不能运行,它只能包含在程序中,只能在程序中执行。一个线程在程序运行时,必须争取到为自己分配的系统资源,如执行堆栈、程序计数器。
多线程
多线程时相对于单线程而言的,指的是在一个程序中可以定义多个线程并同时运行他们,每个线程可以执行不同的任务。
与进程不同的是,同类多线程共享一块内存空间和一组系统资源,所以,系统创建多线程花费单价较小,因此,也称线程为轻负荷进程。

多线程的特点
并行性(同时)
实时性(及时)
进程与多线程比较

例子
进程举例
同时运行的word和ppt进程间没有公共数据(内存)
进程面向不同的软件
线程举例
网络聊天服务器软件为每一个用户采用一个线程及时接收和转发该用户信息
浏览器在下载数据的同时还可以浏览其他网页
Java的无用单元收集器
线程面向一个软件内的不同事物
线程的生命状态周期
同进程一样,线程也有从创建、运行到消亡的过程,称为线程的生命周期
线程的状态
创建
用new运算符创建一个线程后,该线程处于新建状态
该线程仅是一个空对象,并未得到系统的资源
可运行
用start方法启动一个线程后,系统为该线程分配除处理机外的所有资源
线程进入就绪队列排队,等待处理器的调度
运行中
轮到线程占用CPU资源时,JVM将CPU使用权切换给该线程,该线程就可以脱离创建它的主线程开始自己的生命周期。
JVM执行该线程的run()方法
挂起(阻塞)
本线程让出CPU使用权,JVM将CPU资源切换给其他线程
在引起阻塞的原因没有取消前,即使处理器空闲,该线程也不会被执行
当引起阻塞的原因被取消时,线程会重新转入runnable状态,进入就绪队列等待再次被调度。
引起阻塞的原因
1.线程在运行期间执行sleep方法使自身进入休眠状态。休眠指定时间后,重新进入就绪队列等待CPU资源,以便从中断处执行。
2.线程执行期间,执行wait()方法使自身进入等待状态。必须由其他线程执行notify()或notifyAll()方法通知它,才能重新进入就绪队列等待CPU资源,以便从中断处继续执行
3.线程执行期间,执行某个操作进入阻塞状态(如执行读/写操作),只有当引起阻塞的原因消除时,线程才重新回到线程队列中排队等待CPU资源
4.线程执行期间,访问某同步方法,而该方法其他线程正在访问,引起阻塞。
死亡
线程运行结束后进入死亡状态
导致线程死亡的原因
1.自然撤销:线程执行完run()方法
2.强制终止:强制run()方法结束。可以调用线程的stop()方法来强制杀死一个线程,但不推荐使用。
3.被停止:应用程序因故停止运行时,系统将终止该程序正在运行的所有线程。
JVM释放分配给线程的内存
可以使用isAlive方法判断线程是否处于活跃状态
线程的生命周期图

创建一个线程
Java提供了三种创建线程的方法:
通过实现Runnable接口
为了实现Runnable,一个类只需要执行一个方法调用run()

新线程创建之后,调用start()方法才会运行:

例子


通过继承Thread类本身
创建一个线程的第二种方法是创建一个新的类,该类继承Thread类,然后创建一个该类的实例。
继承类必须重写run()方法,该方法是新线程的入口点。必须调用start()方法才能执行。
该方法尽管被列为一种多线程实现方式,但是本质上还是实现了Runnable接口的一个实例。
例子


Thread类的主要方法
public void start()
使线程开始执行;Java虚拟机调用该线程的run方法。
public void run()
如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。
public final void setPriority(int priority)
更改线程的优先级
public final void setName(String name)
改变线程名称,使之与参数name相同
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程
public final void join(long millisec)
等待该进程终止的时间最长为mills毫秒
public void interrupt()
中断进程
public final boolean isAlive()
测试线程是否处于活动状态
Thread的静态方法
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程
public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回true。
public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。
例子
通过实现Runnable接口创建线程

通过继承Thread类创建线程

主程序


通过Callable和Future创建线程
创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
例子


三种创建线程的方式的对比
用Runnable、Callable接口的方式创建多线程时
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
使用继承Thread类的方式创建多线程时,编写简单,如果要访问当前线程,则无需用Thread.currentThread()方法,直接使用this即可获得当前线程。
线程的同步机制与共享资源
Java提供了同步设定功能。共享对象可将自己的成员方法定义为同步化方法,通过调用同步化方法来执行单一线程,其他线程则不能同时调用同一个对象的同步化方法。
生产者和消费者线程同步化问题
使用某种资源的线程称为消费者,产生或释放这个资源的线程称为生产者;生产者生成10个整数,存储到一个共享对象中。消费者把它们打印出来,每生成一个数就随机休息0--100毫秒,然后重复这个过程。生产者生产这10个数到共享对象中,消费者将尽可能快地消费这10个数,即把它们取出后打印出来,这个模型由四个程序组成:
1.共享资源Share的成员变量--非同步模式

2.生产者模式

3.消费者程序

4.主程序

5.修改同步化共享资源Share成员变量

6.栈Stack的共享
临界资源问题


无同步的栈Stack共享的潜在问题
两个线程A和B在同时使用Stack的同一个实例对象,A正在往堆栈里push一个数据,B则要从堆栈中pop一个数据。

最后的结果相当于r没有入栈。
产生这种问题的原因在于对共享数据访问的操作的不完整性。
同步栈SynStack类定义

7.生产者Producer类

8.消费者Consumer类

9.测试栈同步的主程序

Java网络编程
协议
OSI参考模型
1.物理层
检测物理方面的设置
2.数据链路层
控制网络层与物理层之间的通信
3.网络层
将网络地址翻译成对应的物理地址,并决定如何将数据从发送方路由到接收方
4.传输层
负责确保数据可靠、顺序、无错的传达到目的
5.会话层
负责在网络中的两节点建立和维持通信
6.表示层
应用程序和网络之间的翻译官
7.应用层
负责对软件提供接口,以使程序能使用网络服务
TCP/IP协议栈
应用层
HTTP、Telnet、FTP、TFTP、Ping等
提供应用程序网络接口
传输层
TCP/UDP
建立端到端连接
网络层
IP、IGMP、ICMP、ARP/RARP
寻址和路由选择
数据链路层
Ethemet、802.3、PPP、HDLC、FR等
物理介质访问
物理层
接口和线缆
二进制数据流传输
TCP/IP协议与OSI参考模型
对应关系

TCP/IP协议封装过程

网络编程
基本概念
IP地址
标识计算机等网络设备的网络地址,由四个8位的二进制数组成,中间以小数点分隔
如:192.168.1.1
主机名(hostname)
网络地址的助记名,按照域名进行分级管理。
如:www.baidu.com
服务类型(service)
网络的各种服务
如:Web服务、ftp服务、smtp服务、Telent服务
网络编程
利用操作系统在不同通信协议层次上提供的接口(系统调用、库函数)实现网络进程的安全通信。
两个主要的问题
如何准确的定位网络上一台或多台主机
找到主机后如何可靠高效的进行数据传输。
网络进程
就是网点机(连入网络的计算机)上运行的程序。网络进程在通信协议中用端口标识,而它驻留的网点机则用其IP地址或域名来标识。
网络编程模型
是客户机/服务器(C/S)结构
端口
指TCP/IP协议中的端口,是逻辑意义上的概念。主机上每个提供服务的程序都运行在该主机的一个对外开放的端口上,程序员可以创建自己的服务程序时使用自定义的端口,通过这个端口号来连接服务进程以接受服务。
如果把IP地址比作一间房子,端口就是出入这间房子的门。
1.端口常以16位无符号整数编号(0-65535)
2.保留端口(0-1023)和动态端口(1024-65535)
3.一个端口与使用该端口的服务进程是对应的
TCP/IP协议
1.四层机构:物理层、网络层、传输层、应用层
2.协议:IP、TCP、UDP、HTTP、FTP、SMTP等
3.TCP:面向连接的服务。可靠,传输大小无限制,但是需要连接建立时间,差错控制开销大。比如打电话。
4.UDP:无连接的服务。不可靠,差错开销小,传输大小限制在64K以下,不需要建立连接,比如写信
HTTP及URL
统一资源定位器--URL
URL表示Internet上某一资源的地址。
浏览器通过解析给定的URL可以再网络上查找相应的文件或其他资源。
URL的组成

1.协议名(protocol)
指明获取资源所使用的传输协议
如http、ftp、gopher、file
2.Host_name
资源名所在的主机。
3.port_number
连接时所使用的通信端口号
4.file_name
该资源在主机的完整文件名

Java网络编程的三种方式
URL编程
获取URL属性信息
Java网络编程的java.net包
TCP实现网络通信
URL类
URLconnection类
Socket类
ServerSocket类
UDP实现网络通信
DatagramPacket类
DatagramSocket类
MulticastSocket类
URL类
URL构造方法

使用URL对象的字符串URLname来构造URL对象
例如:URL url=new URL("http://www.baidu.com")

通过基地址URL和表示相对路径的字符串构造URL对象
例如:URL index=new URL("http://www.baidu.com")
例如:URL index=new URL(ust,"index.html")

通过协议名、主机名和文件名构造一个URL对象


通过协议名、主机名、端口号和文件名构造一个URL对象

URL类的其他方法
获取URL对象属性的方法:
public String getProtocol()
获取该URL的协议名
public String getHost()
获取该URL的主机名
public String getPort()
获取该URL的端口号
public String getPath()
获取该URL的文件名
public String getFile()
获取该URL的文件名
public String getRef()
获取该URL在文件中的相对位置
public String getQuery()
获取该URL的查询名
例子


创建和使用URL访问网上资源
1.URLConnection类
URL的方法openStream()只能从网络上读取数据,而不能写。
URLConnection是封装访问远程网络资源一般方法类,通过它可以建立与远程服务器的连接,检查远程资源的一些属性。
URL类中的方法openConnection()可以对URL执行的网络资源进行读写。
public URLConnection openConnection();
作用
尝试连接URl指向的网络资源,然后返回封装了操作该连接的类java.net.URLConnection的一个实例
2.URLConnection类中的常用方法
public InputStream getInputStream();
public OutputStream getOutputStream();
public URL getURL();
3.URLConnection读/写编程步骤
1.创建一个URL对象u1
2.使用URLConnection类中的openConnection()方法创建到URL的一个连接对象。
URLConnection u1.openConnection()
成功,返回yigeURLConnection对象
不成功,抛出IOException异常
3.进行数据的读或写操作
4.关闭流
URL访问网络资源例子





InetAddress类代表IP地址

在给定原始IP地址的情况下,返回InetAddress对象

根据提供的主机名和IP地址创建InetAddress

在给定主机名的情况下确定主机的IP地址

返回IP地址字符串(以文本表现形式)

返回本地主机名字符串

返回本地主机

将此IP地址转换为String
例子


Socket编程
基于TCP的C/S
Socket编程
逻辑流程图

Socket连接过程


Socket通信实现步骤
1.创建ServerSocket和Socket
2.打开连接Socket的输入/输出流
3.按照协议(通常是TCP/UDP)对Socket进行读写操作
4.关闭输入/输出流,关闭Socket
Socket通信模式

服务器端ServerSocket类的构造方法

创建绑定到特定端口的服务器套接字

利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口号。

利用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器

创建非绑定服务器套接字
服务器端ServerSocket类的方法

返回此套接字在其上侦听的端口

侦听并接受到此套接字的连接

通过指定超时值启动/禁用SO_TIMEOUT,以毫秒为单位。

将ServerSocket绑定到特定地址(IP地址和端口号)
客户端Socket类的构造方法

创建一个流套接字并将其连接到指定主机上的指定端口号

创建一个流套接字并将其连接到指定IP地址的指定端口号。

创建一个套接字并将其连接到指定远程主机上的指定远程端口

创建一个套接字并将其连接到指定远程地址上的指定远程端口

通过系统默认类型的SocketImpl创建未连接套接字
客户端Socket类的方法

将此套接字连接到服务器,并指定一个超时值。

返回套接字连接的地址

返回此套接字连接到的远程端口

返回此套接字绑定到的本地端口

返回此套接字连接的端点的地址,如果未连接则返回null

返回此套接字的输入流

返回此套接字的输出流

关闭此套接字
Server服务器端
1.创建ServerSocket对象,同时绑定监听端口
2.通过accept()方法监听客户端的请求
3.建立连接后,通过输入流读取客户端发送的请求信息。
4.通过输出流向客户端发送响应信息。
5.关闭相应资源
例子

Client客户端
1.创建Socket对象,指明需要连接的服务器的地址和端口号
2.建立连接后,通过输出流向服务器端发送请求信息
3.通过输入流获取服务器的响应信息
4.关闭相应资源
例子

简易聊天室例子
服务器端

Socket

线程读消息

线程写消息

客户端

Datagram编程
UDP