Java 核心编程API与高级编程实践
第一章 异常
1.1 异常概述
在程序运行中,经常会出现一些意外情况,这些意外会导致程序出错或者崩溃而影响程序的正常执行,在java语言中,将这些程序意外称为异常,出现异常时称为异常处理。
1.2 异常引入
public class TestEx {
public static void main(String[] args){
String teachers[] ={"柳海龙","孙传杰","孙悦"};
for(int i=0;i<3;i++){
System.out.println(teachers[i]);
}
//修改异常之后的
//for(int i=0;i<teachers.length;i++){
//System.out.println(teachers[i]);
//}
System.out.println("输出完毕!");
}
}
程序出错的原因:数组下标越界,定义了数组长度是3,而使用数组时,却访问了下标为3的第4个数组元素。
修改之后:通过数组的长度来控制循环的次数
1.3.异常分类
·Throwable,所有异常都继承自java.lang.Throwable类,Throwable类有两个直接子类Error类和Exception类
·Error类是Throwable类的子类,是Java应用程序本身无法恢复的严重错误。通知用户并终止程序的运行
·Exception是由应用程序抛出和处理的非常严重错误。称之为异常。异常可分为运行时异常和和检查时异常。
·运行时异常(RuntimeException),运行时异常即程序运行时抛出的异常,这个情况编译时是能够通过的
·checkException,检查时异常又称为非运行时异常,这样的异常必须在编程时进行处理,否则编译不通过。
1.4异常处理
基本异常处理
java对异常的处理采取的是抛出、捕获的机制,即由一段可能抛出异常的程序抛出异常(也可能正常执行,不抛出异常),在这段程序外有专门的异常处理程序进行处理,针对抛出的不同类型的异常捕获后进行处理,这就是Java异常处理机制。语句形式如下:
try{
//可能抛出异常的语句块
}catch(SomeException1 e){ //SomeException1特指某些异常,非Java中具体异常
//当捕捉到SomeException1 类型的异常时执行的语句块
}catch(Exception2 e){
//当捕捉到SomeException2 类型的异常时执行的语句块
}finally{
//无论是否发生的异常都会执行的代码
}
1.5多个catch块
看例子:
package prctice;
import java.util.*;
public class TestEx2 {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
try{
String teachers[] ={"柳海龙","孙传杰","孙悦"};
for(int i=0;i<teachers.length;i++){
System.out.println(teachers[i]);
}
System.out.println();
System.out.println("*****");
//给孩子们分苹果
int appleNum=0;
int stuNum=0;
System.out.println("现在给孩子们分苹果");
System.out.println("请输入桌子上有几个苹果:");
appleNum=in.nextInt();
System.out.println("请输入班上有几个学生:");
stuNum=in.nextInt();
System.out.println("现在班上每个孩子能分到 "+appleNum/stuNum+" 个苹果");
}catch(ArrayIndexOutOfBoundsException e){ //捕获数组下标越界异常
System.out.println("数组下标越界,请修改程序");
}catch(ArithmeticException e){ //捕获算数异常
System.out.println("算术异常,请检查程序!");
}
System.out.println("程序执行完毕");
}
}
1.5.1上面程序分析
程序中明明抛出除数为0的算数异常,但显示却是“数组下标越界,请修改程序”。出现的原因是该程序catch语句后面捕获的是Exception类型的异常,即捕获所有类型的异常它是父类。
1.6 finally语句
SOCKET编程{客户/服务器程序
package prctice;
import java.io.*;
import java.net.*;
public class TestEx2{
public static ServerSocket ss=null; //声明服务器端套接字对象
public static void main(String[] args) throws IOException{
try{
//实例化服务器端套接字,服务器端套接字等待请求通过网络传入
ss=new ServerSocket(5678); //其中5678是端口号
//侦听并接受此套接字的连接
Socket socket =ss.accept();
//省略其他代码
//当发生某种IO异常时,抛出IOException异常
}catch(IOException e){
//省略其他代码
}finally{
ss.close();//关闭此套接字
}
}
1.6.1 程序分析
在try语句块中实例出一个服务器端套接字并进行处理,如果try语句块中出现 IOException 异常,正常执行
使用finally语句块,保证了try语句块是否出现异常,finally语句都会被执行,本例子服务器套接字ss对象都会被关闭
在try...catch...finally 异常处理结构中,try语句块是必须的,catch和finally语句块必须出现其中的一个
finally语句块不执行唯一的一种情况是遇到System.exit(1) 表示退出JVM,finally语句块不再执行。
1.7 异常捕获顺序
在捕获异常的时候,应该按照“从小到大”的顺序进行捕获异常,这样才能保证逐层捕获
1.8常见的异常
(1)NullPointerException 空指针异常 属于运行时异常。就是调用未经初始化的对象或者不存在的对象,或是访问或修改null对象的属性或方法。
(2)ClassNotFoundException 没能找到的异常。一般是三种原因:一是的确不存在该类;二是开发环境做客修改,例如目录的结构发生了变化 三是修改类名时没有修改调用该类的其他类,导致类找不到
(3)IllegalArgementException 表明向方法传递了一个不合法或不正确的参数
(4)InputMismatchException 由Scanner跑出,表明Scanner获取的内容与期望类的模式不匹配,或者该内容超出类型范围
(5)IllegalAccessException 当应用程序试图创建一个实例、设置或者调用一个方法,但当前正在执行的方法无法访问指定的类、属性、方法
(6)SQLException 数据库访问错误或者其他错误信息异常
(7)ClassCastException 对象强制转换为不是实例的子类时
(8)IOException 当发生I/O异常时,抛出
1.9 手工抛出异常 与声明方法抛出异常 自定义异常 类的使用
1.9.1手工抛出异常
语法格式 throw new NullPointerFoundException()
public class Test {
public static void main(String[] args) {
System.out.print("Now ");
try {
System.out.print("is");
throw new NullPointerException(" the ");
} catch (NullPointerException e) {
System.out.print(e.getMessage());
}
System.out.print("time.
");
}
}
1.9.2 声明方法异常
所谓声明方法抛出异常,就是当方法本身不知道或者不愿意处理某个问题跑出的异常时,可以选择用throws 关键字将该异常提交给调用方法进行处理。需要在参数列表后面在方法体的大括号前增加“throws 异常列表”
public class Test {
public static void main(String[] args) throws Exception{
System.out.print("Now ");
try {
System.out.print("is");
} catch (NullPointerException e) {
System.out.print(e.getMessage());
}
System.out.print("time.
");
}
}
1.9.3 自定义异常类
Exception类时Java中所有异常类的父类,所以自定义异常类时,通过继承自该类。
//自定义异常类
public class AgeException extends Exception{
private String message;
//自定义异常类构造方法
public AgeException(int age) {
// TODO Auto-generated constructor stub
message = "年龄设置为"+age+"不合适!";
}
public String toString(){ //自定义异常类toString()方法
return message;
}
}
//对象人类
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException{
if(age<=0||age>=120){
throw new AgeException(age);//抛出AgeException自定义异常
}else{
this.age = age;
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(150);
System.out.println("正确年龄为"+p.getAge());
} catch (AgeException e) {
// TODO: handle exception
System.out.println(e.toString());
}
}
}
第二章 数据结构
2.1 数据逻辑结构
数据结构可以分为逻辑结构和存储结构。逻辑结构还可以分为四类。
集合:数据元素间没有任何元素
线性结构:数据元素之间有线性关系,所谓线性关系是指除第一个元素外,其他元素有且只有一个前驱,除最后一个元素外,其他元素有且只有一个后继
树结构:数据元素间有层次关系
图结构:数据元素间有网状关系
第三章 集合和泛型
3.1集合框架
集合是一种逻辑结构
集合也称为容器,他可以将一系列元素组成一个单元,用于存储、提取、管理数据。JDK提供的集合API都包含在java.util 包内
Java集合的框架主要分为两大部分,一部分实现Collection接口,该接口定义了存取一组对象的方法,其子接口Set和List分别定义了存取方法;另一部分是Map接口,该接口定义了存储一组“键(key)值(value)”映射对的方法
3.2Collection接口框架
·Collection是最基本的集合接口,一个Collection代表一组Object,每个Object即Collection中的元素。
·Collection接口继承自Iterator接口,因为Iterator接口对象允许成为foreach语句的目标
·Collection接口主要有三个子接口:分别是List接口、Set接口、Queue接口
3.2.1 List接口
定义及其特点:
主要有三种:ArrayList、LinkedList、Vector;这三种都是List接口的实现类,使用上完全一样,只是实现原理不同,效率不同。
(元素有序可重复序列)
实现List接口的结合是一个有序的Collection序列。操作此接口的用户可以对这个序列中每个元素的位置进行精确控制,用户可以根据元素的索引访问元素。List接口中的元素可以使重复的。
>ArrayList特点:
底层数组实现查找快,增、删慢;线程不安全,效率高
>LinkedList特点
底层链表实现;增、删、快、查找慢
>Vector 特点
线程安全(线程同步),效率低
可以容纳所有类型的对象,根据用户添加对象的顺序来输出
3.2.2 Set接口
定义:
Set集合无序,不允许有重复元素
Set集合通过存入的对象的equals方法来保证集合中没有重复的元素
实现set接口的集合是一个无需的Collection序列,该序列中的元素不可重复。因为Set接口是无序的,所以不可以通过索引来访问Set接口中的数据元素。
Set接口的一些方法:
1.boolean add(Object o):向集合中添加元素
2.void clear() :移除集合中所有的数据元素,即将集合清空
3.boolean contains(Object o):判断集合中是否包含该数据元素,返回类型是布尔类型。
4.boolean isEmpty() 判断集合是否为空 布尔类型
5.Itreator iterator() 返回一个iterator对象,可以用它来遍历集合中的数据元素。
6.boolean remove (Object o) :如果此集合中包含该数据原素,将其删除,并返回boolean
7.int size():返回集合中的数据元素个数
8.Object[] toArray() 返回一个数组,该数组包含集合中所有的数据元素
#HashSet
HashSet是Set的实现类,因此也没有重复元素;底层使用哈希算法保证没有重复元素.
#TreeSet(默认是升序)
TreeSet通过二叉树算法保证无重复元素,并对元素进行排序(默认是升序);
在使用Treeset时必须指定比较的算法,指定的方式有两种:
自然排序:将要存储的类实现Comparable接口,重写compareTo方法,在方法中指定的算法。
比较器排序:在创建TreeSet时,传入一个比较器Comparator,在比较器的compare方法中制定算法。
第一种比较方式:让元素自身具备比较性原酸需要实现Comparable接口,覆盖comparaTo方法这种方式也成为元素的自然排序,或者叫做默认顺序。
第二种比较方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让元素自身具备比较性。在集合初始化时,就有了比较性。
自定义比较器:
1.定义一个类
2.实现Comparable接口
3.覆盖compareTo方法
3.2.3.Queue接口
该接口用于处理元素钱保存元素的Collection序列。除了具有Collection接口基本的操作外,Queue还提供了其他的插入、提取和检查等操作。
3.3 Map接口框架
Map接口和Collection接口的本质区别在于:Collection接口存放的是一个个对象,Map接口里面存放的是一系列的键值对。Map接口集合中的key不要求有序,但是不能重复。每个键最多能映射到一个值
3.4 Map集合
一组成对的“键值对”对象,允许根据键(key)来查找值(value)
Map集合的键唯一,值可以重复,所以Map的所有键构成了一个Set集合
可以通过集合存储元素使用put(key,value) 方法
#HashMap
线程不安全,存取速度快,允许存放null键,null键
通过HashSet原理保持键唯一性
HashMap是无序的
Map接口的方法:
·put(Object key,Object value) //将指定的键值对添加到Map集合中,如果此Map集合以前包含,则就会替换。
·get(Object key) //返回指定的键对应的值,如果此Map集合不包含该键,则返回null
·Set keySet() 返回此Map集合中包含的键的Set集合。 集合名字.keySet() //输出得到key键的所有值
·Collection values() //返回Map集合中的值Collection集合 同上
int size() 表示集合中的元素个数
boolean containsKey(Object key)//包含指定的键值对返回true
map集合的两种取出方式
Set keySet:将map所有的键存入到Set集合。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合。再通过迭代器取出。
Set entrySet:将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry
Entry其实就是Map中的一个static内部接口
问题:为什么定义在内部呢?
因为只有有了Map集合,有了键值的映射关系。关系属于Map集合中的一个内部事物。而且该事物在直接访问Map集合中的元素。
Map.Entry 其实是Entry也是一个接口,他是Map接口中的一个内部接口
3.5Iterator迭代器接口
通过集合对象的 itreator() 方法获得迭代器Iterator
通过Itrator迭代器的 hasNext() 方法判断是否存在下一个元素
通过Itrator迭代器的 next() 方法获取下一个元素
语法格式:
Iterator it = 集合名.iterator();
while(it.hasNext()){
system.out.println(it.next());
}
3.6 Comparable接口 java中的compareTo方法
·如果我们想要定义自己排序方式,加入TreeSet集合中的对象所属的类实现Comparable来解耦,通过实现compareTo(Object o) 方法来实现。
·Comparable称为内部比较器
·Comparator 外部比较器
```
####3.6.1 当两个比较的字符串是英文且长度不等时
```
1)长度短的与长度长的字符一样,则返回的结果是两个长度相减的值
a="hello";
b="hell";
num=1;
或者
a="h";
b="hello";
num=4;
2)长度不一样且前几个字符也不一样,从第一位开始找,当找到不一样的字符时,则返回的值是这两个字符比较的值
a="assdf";
b="bdd";
num=-1;
```
#####3.6.2 当两个比较的字符串是英文且长度相等时
```
1)一个字符
a="a"; //97
b="b"; //98
num=-1;
2)多个字符,第一个字符不同则直接比较第一个字符
a="ah"; //a=97
b="eg"; //e=101
num=-4;
3)多个字符,第一个字符相同则直接比较第二个字符,以此类推
a="ae"; //e=101
b="aa"; //a=97
num=4;
```
####3.7增强版for循环
```
for(类型 对象 : 集合或数组){ }
或者
for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
{
}
//对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。这个方法无法实现对集合元素的删除。需要调用迭代器的remove() 删除
```
####3.8 ArrayList的使用
```
数组是连续存储的
ArrayList实现了List接口,在存储方式上实现采用数组进行顺序存储,实现了可变长度的数组。与LinkedList不同的是,LinkedList采用的是链式存储结构
```
#####3.8.1LinkedList的使用
```
void addFirst(Object o) 将指定数据元素插入此集合的开头
void addLast(Object o) 将指定数据元素插入此集合的结尾
Object getFirst() 返回集合的第一个元素
Object getLast() 返回集合的最后一个元素
Object removeFirst() 删除集合的第一个元素
Object removeLast() 删除集合的最后一个元素
```
#### 3.9Collections工具类
```
特点:类中的方法都是静态的,不需要创建对象,直接使用类名调用即可
Coloections
工具类,提供了对集合的常用操作
对集合进行查找‘
取出集合中的最大值、最小值
对List集合进行排序
常用方法:
void sort(List list) //根据元素自然顺序对指定集合按升序进行排序
void shuffle(List list) //进行随机排序
void reserve(List list) //反转指定元素
Object max(Collection coll) //根据元素自然排序,返回给定Collection集合中的最大元素
Object min(Collection coll) //根据元素自然排序,返回给定Collection集合中的最小元素
int binarySearch(List list,Object o) //是用二分法查找指定的集合,以获得指定数据元素的索引
int indexOfSubList(List resource,List target) //返回值指定源集合中第一次出现指定目标集合的起始位置,如果没有出现就返回-1
int lastOfSubList(List resource,List target) //返回值指定源集合中最后一次出现指定目标集合的起始位置,如果没有出现就返回-1
void copy(List list,List src) 将所有数据元素从一个集合复制到另一个集合
void fill(List list,Object o) 使用指定数据元素替换指定集合中的所有数据元素
void swap(List list ,int i,int j) //指定位置交换数据元素
```
#####3.9.1Collections和Collection有什么区别?
```
Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。
Collection是个java.util下的接口,它是各种集合结构的父接口。
Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
它有两个常用的子接口,
List:对元素都有定义索引。有序的。可以重复元素。
Set:不可以重复元素。无序。
通常常用的集合都是线程不安全的。因为要提高效率。
如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
```
####3.10Arrays工具类
```
Arrays类是操作数组的工具类,和Collections工具类相似,他提供的所有方法都是静态的
主要功能:
对数组进行排序
给数组赋值
比较数组中元素的值是否相等
进行二分查找
特点:类中的方法都是静态的,不需要创建对象,直接使用类名调用即可. Arrays.fill(arr,8);//表示填充arr数组的空间用数字8
是数组的工具类,提供了操作数组的工具方法;
对数组进行排序对数组进行二分查找、对数组转为字符串显示形式
```
####3.11 自动拆箱和装箱
```
自动拆箱和装箱就,目的是方便基本数据类型和其对应的包装类型之间的转换。
```
####3.12泛型
```
概念:
由于集合可以存储不同类型的数据,所以取元素时可能会导致转换错误;用的是引用数据类型
集合一旦声明了泛型,便只能存储同一类型的对象
```
**好处**
```
将运行时期出现问题ClassCastException,转移到了编译时期,方便我们解决运行时问题减少安全;
避免了转换麻烦。
```
**使用**
```
什么时候定义使用泛型?
当类中要操作的引用数据类型不确定时,早期定义Object来完成拓展
```
####3.13把数组变成list集合有什么好处?
```
asList:将数组变成list集合
可以使用集合的思想和方法来操作数组中的元素。
注意:将数组变成集合,不可以使用集合的增删方法。
因为数组的长度是固定。
contains。
get
indexOf()
subList();
如果你增删。那么会反生UnsupportedOperationException,
```
####3.14静态导入
```
Static import 静态导入
当类名重名时,需要指定具体的包名。
当方法重名时,指定具备所属的对象或者类。
import java.util.*;
import static java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。
import static java.util.Collections.*;
import static java.lang.System.*;//导入了System类中所有静态成员。
```
****
###第四章 IO/XML
####4.1File类
```
Java是面向对象的语言,要想把数据存到文件中,就必须要有一个对象表示这个文件。File类的作用就是代表一个特定的文件或目录,并提供了若干方法对这些文件或目录进行各种操作。File类在java.io包下,与系统特定输入输出的相关的类都在此包下。
```
####4.2File类构造方法
```
构造一个File类的实例,并不是创建这个目录或文件,而是创建该路径(目录或文件)的一个抽象,他可能真实存在,也可能不存在
(1)File(File parent,String child)
根据parent 抽象路径名个cjild路径名字字符串创建一个新的File实例
(2) File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新的File实例
(3)File(URI uri)
通过将给定的URI类对象转化为一个抽象路径名来创建一个新的File类实例
```
**注意:1.在创建File类的实例的时候Java语言的一个显著特点是“”一处编译,处处运行“”,所以在使用File类的创建的时候需要保证这个File类也是跨平台的。但是不同的操作系统对文件路径的设定各有不同的规定在**
**Windows系统写的文件路径是 C:comdTest.java 但是在Linux和Unix操作系统下文件的路径可能是/home/bd/Test.java **
####4.3.File类提供了一些静态属性,通过这些静态属性,可以获得Java虚拟机所在操作系统的分隔符相关信息
```
(1) File.pathSeparator 与系统有关的路径分隔符,它贝表示为一个字符串
(2)File.pathSeparatorChar 与系统有关的路径分隔符,它贝表示为一个字符
(3) File.separator 与系统有关的默认名称分隔符,它贝表示为一个字符串
(4)File.SeparatorChar 与系统有关的默认名称分隔符,它贝表示为一个字符
```
**举例**
```
package com.lanqiao.demo1;
import java.io.File;
public class TestFileSeparator {
public static void main(String[] args) {
System.out.println("PATH分隔符:"+File.pathSeparator);//显示PATH的分隔符 " : "
System.out.println("路径分隔符:"+File.separator); //显示路径分隔符: " / "
}
}
```
####4.4 File类使用
```
package com.lanqiao.demo1;
import java.io.File;
import java.io.IOException;
public class TestFile {
public static void main(String[] args) throws Exception {
System.out.println("文件系统根目录");
for(File root : File.listRoots()){
//format方法使用指定格式化字符串输出
System.out.format("%s",root);
}
System.out.println();
showFile();
}
public static void showFile() throws Exception {
//创建文件对象file,注意使用转义字符
File f = new File("C:\com\bd\Tets.java");
File f1 = new File("C:\com\bd\Tets1.java");
//当文件不存在时,创建一个新的空文件
f1.createNewFile();//在这里需要抛出异常
//指定输出格式
System.out.format("输出字符串:%s%n",f);
System.out.format("判断File类对象是否存在:%b%n",f.exists());
//%tc,输出日期和时间 %b 是布尔类型
System.out.format("获取File类对象最后修改时间:%tc %n", f.lastModified());
System.out.format("判断File类对象是否是文件:%b %n",f.isFile());
System.out.format("判断File类对象是否是目录:%b%n",f.isDirectory());
System.out.format("判断File对象是否有隐藏的属性 :%b%n",f.isHidden());
System.out.format("判断File对象是否是可读:%b%n",f.canRead());
System.out.format("判断File对象是否可写:%b%n",f.canWrite());
System.out.format("判断File对象是否可执行:%b%n",f.canExecute());
System.out.format("判断File对象是否是绝对路径:%b%n",f.isAbsolute());
System.out.format("获取File对象的长度:%d%n",f.length());
System.out.format("获取File对象的路径:%s%n",f.getPath());
System.out.format("获取File对象的绝对路径:%s%n",f.getPath());
System.out.format("获取File对象父目录的路径:%s%n",f.getParent());
}
}
```
####4.5 静态导入
#####4.5.1 静态导入前
```
//静态导入的特性:用来导入指定累的某个静态属性或方法或全部静态属性或方法,静态导入使用import static
package com.lanqiao.demo1;
public class TestStatic {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.toHexString(12));
}
}
```
#####4.5.2静态导入后
```
package com.lanqiao.demo1;
import static java.lang.System.out;//标记
import static java.lang.Integer.*;//标记
/**
* 静态导入后的代码
*
* @author qichunlin
*
*/
public class TestStatic2 {
public static void main(String[] args) {
out.println(MAX_VALUE);
}
}
```
**通过对比分析的:使用静态导入省略了System和Integer的书写,编写代码相对很简单,在使用静态导入的时候需要注意以下几点:**
```
1.虽然在语言表述上说的是静态导入,打奶代码中必须写import static
2.防止静态导入冲突。例如:同时对 Integer 和 Long 类执行了静态导入,引用MAX_VALUE属性将导致一个编译器错误,编译器不知道使用哪个 M AX _VALUE
3.虽然静态导入让代码编写相对简单,但毕竟没有完整的写出静态成员所属类名,程序的可读性降低。
```
####4.6 获取目录和文件
**File类提供了一些方法,用来返回指定路径下的目录和文件。**
```
·String[] list() //返回字符串数组,这些字符串指定抽象路径名表示的目录中的文件个目录。
·String[] list(Filename filter) //返回一个字符串数组,这些字符串指定此抽象名表示的目录中满足指定过滤器的文件和目录
·File[] listFiles() //返回一个抽象路径名数组,这些路径名表示次抽象路径名表示的文件和目录
·File[] listFiles(FilenameFilter fileter) //返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
```
####4.7 字符流和字节流
#####4.7.1 I/O流
```
在Java中,文件的输入输出是通过流来实现的,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
一个流必须有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至是Intenet上某个URL。对于流而言,不用关心数据是如何传输的,只需要向源端输入数据,从目的端接收数据即可.
```
![Alt text](./1528550834449.png)
#####4.7.2 如何理解输入输出呢?
```
简单地说:你听别人说话就是输入,你开口跟别人说话就是输出。在计算机的世界中,输入Input 和输出Output 都是针对计算机的内存而言。比如:读取计算机硬盘上的一个文件对于内存而言就是输入;向控制台打印输出一句话就是出书。所以将这类方法叫做输入输出即 I / O
```
```
·流是对IO操作的形象描述,信息从一处转移到另一处就叫做 I/O 流
·输入流的抽象表示形式是接口InputStream;输出流的抽象表示形式是接口OutputStream
·System.out.println(); //是典型的输出流,向控制台输出信息
·new Scanner(System.in); //是典型的输入流duq控台输入的信息 它们两者是InputStream和OutputStream的实例对象
```
#####4.7.3字符流和字节流的区别
```
按照处理数据的单位,流可以分为字节流和字符流。字节流的处理的单位是字节,通常用来处理二进制文件,如文件,图片文件。而字符流的处理单位是字符,因为Java采用Unicode 编码 Java字符流处理的即Unicode字符,所以在操作汉字、国际化方面字符流具有优势
```
#####4.7.4字节流
```
所有的字节流类都继承自InputStream或OutputStream两个抽象类
FileInputStream 把一个文件作为输入源,从本地文件系统中读取数据字节,实现对文件的读取操作
ByteArrayInputStream 把内存中的一个缓冲区作为输入源,从内存数组中读取数据字节
PipeInputStream:实现管道的概念从线程管道中读取数据字节。主要在线程中使用,用于两个线程间的通信;
SequenceInputStream 其他输入流的逻辑串联
System.in 从用户控制台读取数据字节,在System类中,in是InputStream类的静态对象
```
#####4.7.5 字节流实现文件的复制
```
package com.lanqiao.demo9;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
File f = new File("D:/test.txt");
//String 构造输入流
in = new FileInputStream("D:/test.txt");
//通过构造方法File类构造输出流
out = new FileOutputStream("D:/t.txt");
//通过逐个读取、存入字节、实现文件的复制
int num;
while((num=in.read())!=-1){
out.write(num);//写到文件里面去
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
}
}
}
分析:
read()方法碰到数据流末尾返回的是-1,二是在输入、输出流用完之后,要在一场处理的finally快中关闭输入输出流。
```
#####4.7.6 字符流
```
字符流和字节流都是无缓冲的输入输出流,这就意味着每次的读写都交给操作系统来处理。
所有的字符流都继承自Reader和Writer这两个抽象类,Reader用于读取字符流的抽象类,子类必须实现的方法有read(char[],int,int)和close ,Writer是用于写入字符流的抽象类。
FileReader 从文件系统中读取字符序列
CharArrayReader 与ByteArrayInputStream对应,从字符数组中读取数据
StringReader 从字符串中读取字符序列
```
#####4.7.7 缓冲流
```
缓冲流是一种装饰器类,目的是让原字节流、字符流新增缓冲的功能。以字符流举例说明,字符缓冲流从字符流中读取写入字符,不立刻要求 系统进行处理,而是缓冲部分字符,从而实现按规定字符数、按行等方式的高效读取或写入。缓冲区大小可以指定,也可以使用默认的
通过一个输入字符流和输出字符流创建输入字符缓冲流和输出字符缓冲流
BufferedReader in = new BufferedReader(new FileReader("D:/t.txt"));
BufferedReader out = new BufferedWriter(new FileWrier("D:/t.txt"));
缓冲流和字符流/字节流的区别:
new 的方式不一样 FileInputStream fis = new FileInputStream("Car.java");//缓冲流
装饰器类 in = new 装饰器类(fis);
FileOutputStream out = new FileOutputStream("Car.java");
读取文件的形式不一样 String str;//缓冲流
while((str.readLine())!=null){out.write(str); out.newLine();}
int num;//字节流
while((num = in.read())!=-1) {out.write(num);}
例子:
package com.lanqiao.demo9;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
File f = new File("D:/test.txt");
in = new BufferedReader(new FileReader("D:/test.txt"));
out = new BufferedWriter(new FileWriter("D:/t.txt"));
//逐行读取、存入字符串、实现文件复制
String str;
while((num=in.readLine())!=null){
out.write(str);
//写入一个分行符,否则内容在一行显示
out.newLine();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
}
}
}
还可以调用flush() 方法就是刷新该流的缓冲
还可以字节流转成字符流 (是一种适配器模式)
package com.lanqiao.demo9;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class T {
public static void main(String[] args) throws IOException {
BufferedReader in = null;
try {
//将字节流通过InputStreamReader转换成字符流
in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("说出你想说的话");
String str = in.readLine();
System.out.println("你最想表达的是"+str);
} catch (Exception e) {
System.out.println(e.getMessage());
}finally{
if(in!= null){
in.close();
}
}
}
}
```
#####4.7.8 数据流 、字节打印流、字符打印流
```
·数据流简单来说就是容许字节流字节直接操作基本数据类型和字符串
·打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
·字符打印流
PrintWriter
构造函数可以接收的参数类型:
1.file对象。File
2.字符串路径。String
3.字节输出流。OutputStream
4.字符输出流,Writer。
·字节打印流
PrintStream
构造函数可以接收的参数类型:
1.file对象。File
2.字符串路径。String
3.字节输出流。OutputStream
```
####4.8 XML
```
XML是可拓展标记语言(eXtensible Markup Language)
```
####4.8.1 应用范围与文档结构形成
```
存储数据:内存中的数据需要存储到文件中,在关闭系统或系统掉电之后,可以用来恢复XMl与数据库相比最大的优势就是简单、通用
系统配置:使用XMl文档进行系统配置,配置修改时不需要重新编译,灵活性强
数据交换:标准的XML文件可以进行导入导出已达到交换数据的目的
XML的文档结构
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person>
<name>小明</name>
<age>12</age>
<city>罗湖区</city>
<province>深圳</province>
</person>
<person>
<name>小陈</name>
<age>20</age>
<city>宝安区</city>
<province>湖南</province>
</person>
<person>
<name>小李</name>
<age>23</age>
<city>韶关</city>
<province>湖南</province>
</person>
<person>
<name>小赵</name>
<age>21</age>
<city>武汉</city>
<province>湖北</province>
</person>
</persons>
```
#####4.8.1 DTD文档类型定义
```
DTD 分为两类:外部 DTD和内部 DTD
内部DTD文档::<!DOCTYPE 根元素 [定义内容]
外部DTD文档:<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
内部DTD实例
<?xml version='1.0' encoding='gb2312'?>
<!DOCTYPE poem[ <----------------根元素的名称
<!ELEMENT poem (author,title,content) ><---------------------子元素的名称及顺序
<!ELEMENT author (#PCDATA)><------------------子元素的数据类型
<!ELEMENT title (#PCDATA)>
<!ELEMENT content (#PCDATA)>
]> <------------------------结束标签
<poem>
<author>王维</author>
<title>鹿柴</title>
<content>
空山不见人,但闻人语声.
返景入深林,复照青苔上.
</content>
</poem>
```
####4.9 XML解析 有四种方法 (1.SAX 2.DOM 3.Jdom 4.dom4J)
```
Jdom创建XML文档的步骤:
1、创建文档
2、创建根节点
3、往根节点中添加子节点
4、把根节点绑定到文档上
5、保存生成的xml文档(IO流)
Jdom实现XML元素增删改查的步骤:
1、创建工厂
2、获取文档
3、获取根节点
4、往根节点中添加新的子节点(添加) 或 获取根节点下所有的子节点(删、改、查)
5、重新保存
```
****
###第五章 反射机制
```
在运行时动态获取类的信息以及动态调用对象的方法的功能成为Java的反射机制。在Java中,只要给丁类的名字,那么就可以通过反射机制来获得类的所有信息
```
####5.1 Class对象的三种方式
```
对象名.getClass()
类名.class
Class.forName
实例化对象
以前的做法:类名 对象名 = new 类名();
反射的做法: Class<?> c1 = Class.forName("com.lanqiao.ddemo.Person");
Person per1 = (Person)c1.newInstance();
通过反射获取类中常见的元素
获取构造方法
获取属性
获取方法
反射调用方法的步骤
获得class对象
通过class对象获得实体对象
通过Class对象访问指定的某个方法
通过方法对象去执行,返回方法的值
反射调用属性的步骤
获得class对象
通过class对象获得实体对象
获得指定的属性
通过属性调用get(Object obj) 方法执行,返回Object类型
```
####5.2 Class类常用方法
```
Field[] getFields() 返回一个包含field对象的数组,存放该类或接口的所有可访问公共属性(含继承的共有属性)
Field[] getDeclareFields() 返回一个包含Field对象的数组,存放该类或接口的所有属性(不含继承)
Field getField(String name) 返回指定公共属性名的Field对象
Method[] getMethods() 返回一个包含Method对象的数组,存放该类或接口的所有可访问公共方法(含继承的公共方法)
Constructor getConstructor(Class[] args) 返回一个指定参数列表的Constructor对象
Class[] getInterfaces() 返回一个包含Class对象的数组,存放该类或接口实现的接口
String getName() 以String的形式返回该类(类、接口、数组类、基本数据类型或void)
```
****
###第六章 多线程
####6.1 多线程的概述
```
·一个正在运行的程序对于操作系统而言称为进程(每一个应用程序就是一个进程);程序是一段静态代码,是应用程序执行的蓝本,而进程是指一个正在运行的程序,在内存中运行,有独立的地址空间。
·线程可以称为轻量级进程,他和进程一样拥有独立的执行路径。进程与进程之间是不能数据共享的,一个进程包含了多个线程。线程与线程之间是可以实现数据共享的。
·线程和进程的区别是线程存在于进程中,拥有独立的执行堆栈和程序计数器
```
#####6.1.1 线程引入
```
·在操作系统中,使用进程是为了使多个程序能并发执行,以提高资源的利用率和系统的吞吐量。
·在操作系统中,再引入线程,则是为了减少采用多进程方式并发执行时所付出的系统开销,使计算机操作系统具有更好的并发性。
·进程是一个资源的拥有者,因而在创建、撤销和切换中,系统必须为之付出较大的系统开销。
·线程是操作系统中的基本调度单元,进程不是调度的单元;进程是被分配并拥有资源的基本单元,同一进程内的多个线程共享该进程的资源,但线程并不拥有资源,只是使用他们。由于共享资源,所以线程间需要通信和同步机制
```
####6.2 多线程的优势
```
多线程就是,在某个时间片段同时有多个任务在一起执行,一个进程中有多个线程,称为多线程。
优势:
1.系统开销小
2.方便通信和资源共享
3.简化程序
4.多线程技术使程序的响应更快,用户在进行其他工作时一直处于活动状态
5.当前没有进行处理的任务时可以将处理器时间让给其他任务
6.占用大量处理时间的任务可以顶起将处理器时间让给其他任务
7.可以随时停止任务
8.可以分别设置各个任务的优先级以优化性能
缺点:
等候使用共享资源时造成程序的运行速度变慢。这些共享资源住哟啊是独占性的资源。如打印机等。
2.对线程进行管理要求额外的CPU开销。线程的使用会给系统带来上下文切换的额外负担
3.线程的死锁。较长时间的等待或资源竞争以及思索等多线程症状
```
####6.3何时使用线程
```
1) 耗时或大量占用处理器的任务阻塞用户界面操作;
2) 各个任务必须等待外部资源 (如远程文件或 Internet连接)。
```
#####6.3.1两种实现多线程的方式
```
1.继承Thread类
定义一个类继承Thread类
复写Thread类中的run()方法,将线程的任务代码封装到run方法中
直接创建Thread的子类对象,创建线程
调用start()方法,开启线程(调用线程的任务run方法)通
获取线程名称:过Thread的getName()获取线程的名称。
2.实现Runnable接口
定义一个类,实现Runnable类
覆盖接口的run()的方法,将线程的任务带啊封装到run方法中
创建Runnable接口的子类对象
将Runnable接口的子类对象作为参数传递给Thread类的构造函数
调用start()方法,启动线程
在使用Runnable的时候注意,Runnable接口并没有任何对线程的支持,因此还必须创建Thread类的实例,通过Thread类的构造函数将Runnable对象转为Thread对象,从而创建线程类。
语法:类名 对象名 = new 类名();
Thread 线程对象名 = new Thread(对象名);
两种方法的区别:
只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处
• 避免单继承的局限,一个类可以继承多个接口。
• 适合于资源的共享
```
####6.4 线程的生命周期
```
1.新建状态
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。
注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。
2.就绪状态
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
提示:如果希望子线程调用start()方法后立即执行,可以使用Thread.sleep()方式使主线程睡眠一伙儿,转去执行子线程。 sleep()方法
3.运行状态
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。如果该线程失去了cpu资源,就会又从运行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、desyory()方法等等,就会从运行状态转变为死亡状态。
4.阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行:
5.死亡状态
当线程的run()方法执行完,或者被强制性地终止,就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
```
####6.5 线程的状态控制
```
线程睡眠:sleep
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,sleep(1000)表示1秒
线程让步:yield (让线程放弃CPU资源的方法)
和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。
实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程。
线程合并:join
是将几个并行线程的线程合并为一个单线程执行。应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能,注意,它不是静态方法。
线程中断 interrupt
中断线程的阻塞状态(而非中断线程),在使用的过程中,此时sleep() 方法会抛出InterruptedExceotion异常
是否处于活动状态 isAlive()
判断该线程是否处于活动状态,处于就绪、运行、和阻塞状态的都属于活动状态
设置当前线程的优先级
setPriority(int newPriority)
```
#####6.5.1关于sleep()方法和yield()方的区别如下:
```
①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
②、sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
```
**join的两个重载方法**
| 方法 | 说明 |
| :-------- | --------:|
| void join() | 当前线程等该加入该线程后面,等待该线程终止。 |
| void join(long millis) | 当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度 |
#####6.5.2 线程优先级
```
每个线程执行时都有一个优先级的属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会,一般情况下,高级线程更先执行完毕。
方法:
setPriority(int newPriority):更改线程优先级
int getPriority() 返回线程的优先级。
String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组
常量:
(1)MAX_PRIORITY:最高优先级(10级)
(1)Min_PRIORITY:最低优先级(1级)
(1)Morm_PRIORITY:默认优先级(5级)
设置程序优先级,并不能保证优先级高的先执行,也不能保证优先级高的获得更多的CPU资源,只是给操作系统调度程序提供一个建议,运行那个线程是由操作系统决定
```
####6.5.3 如何结束一个线程
```
控制循环语句和判断条件的标识符来结束掉线程
正常执行完run方法
使用interrupt结束一个线程。
在线程中调用interrupt()方法可以自动把默认值false改为true,当程序碰到sleep()方法 时会抛出InterruptedException异常,从而结束线程,可以调用isInterrupted()方法观察 状态,如果值为false则正常,如果值为true,则中断.
```
####6.6
```
守护线程是为其它线程的运行提供便利的线程。Java垃圾回收机制的某些实现就是用了守护线程
```