[JAVA_开课吧资源]第五周 I/O操作、多线程、网络编程技术

主题一 I/O操作

» 流的概念

在面向对象语言中, 数据的输入和输出都是通过数据流来实现的。数据流是一组有顺序、有起点和终点的字符集合。就好比是两个不同的池子,一个池子中存满了水,而另一个池子中则没有任何的东西,在这两个水池中安放一个管子,水就可以从一个池子流向另一个池子了。在从一个池子向另一个池子输送水的过程中,水扮演的角色就是数据流。

[请点击查看更多内容 转自文章]

» Stream

stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源。在Java的IO中,所有的stream(包括Inputstream和Outputstream)都包括两种类型:

  • 字节流:表示以字节为单位从stream中读取或往stream中写入信息,即io包中的inputstream类和outputstream类的派生类。通常用来读取二进制数据,如图象和声音
  • 字符流:以Unicode字符为导向的stream,表示以Unicode字符为单位从stream中读取或往stream中写入信息

[请点击查看更多内容 转自博客]

» 输入输出流

可以读取字节的类就被称为输入流,而且所有的输入流都继承自抽象类InputStream。一个能够将字节流传送出去的类被称为输出流,所有的输出流都继承自抽象类OutputStream。输入流和输出流可以分为字节流类和字符流类。

字节流类包括InputStream和OutputStream两个抽象类。其中包含了字节输入和输出所有的方法。真正用来做数据输入和输出处理的是这两个抽象类的各个具体的子类,这些子类可以根据不同的输入和输出进行处理。

字符流包括Reader和Writer两个抽象类。这些抽象类处理的是Unicode字符流。用来做数据处理的是Reader和Writer的子类。这些子类会根据不同的输入和输出进行处理。

[请点击查看更多内容 转自文章]

» 字节流和字符流的区别

Reader和Writer要解决的,最主要的问题就是国际化。原先的I/O类库只支持8位的字节流,因此不可能很好地处理16位的Unicode字符流。Unicode是国际化的字符集(更何况Java内置的char就是16位的Unicode字符),这样加了Reader和Writer之后,所有的I/O就都支持Unicode了。此外新类库的性能也比旧的好。但是,Read和Write并不是取代InputStream和OutputStream,有时,你还必须同时使用"基于byte的类"和"基于字符的类"。为此,它还提供了两个"适配器(adapter)"类。InputStreamReader负责将InputStream转化成Reader,而OutputStreamWriter则将OutputStream转化成Writer。

[请点击查看更多内容 转自博客]

» Inputstream类和Outputstream类

inputstream类和outputstream类都为抽象类,不能创建对象,可以通过子类来实例化。

[请点击查看更多内容 转自没有盖的盒子的博客]

» 字节输入输出流

InputStream 是字节输入流,也是所有字节输入流的超类,在它的派生类中必须要重新定义字节输入流中所声明的抽象方法。其中有一些方法,如read()方法,是从输入流中来读取一个字节的内容,并且返回类型是整型。

字节输出流OutputStream是所有字节输出流的超类,它是一个抽象类。它的派生类必须重新定义OutputStream中定义的抽象方法。

[请点击查看更多内容 转自博客]

» 字节文件输入、输出流

文件字节输入输出流是FileInputStream和FileOutputStream,这是InputStream和OutputStream的子类。使用这两个类就可以从一个指定的文件中读取或者向某一个文件中写入数据。

[请点击查看更多内容 转自人人网王晓晔的日志]

» 字节缓冲输入、输出流

使用所使用的字节输入流和字节输出流是以字节为单位进行读写操作的,这样一来,就使文件流的效率变的非常低。这时需要使用缓冲输入流(BufferedInputStream)和缓冲输出流(BufferedOutputStream),当输入数据时,将数据存放到一个缓冲区中,当需要对数据进行读写时,就直接去访问缓冲区。当输出数据时,数据不会直接向输出流输出,而是先将数据写入缓冲区,当缓冲区中的数据满时,才会将缓冲区中的数据写入输出流中,这样一来就大大提高了输入和输出操作的效率。

[请点击查看更多内容 转自shazhuzhu1的ITeye博客]

» 字节数据输入、输出流

在流操作中,还有专门针对于java基本数据类型的写入和读取的操作,分别是字节数据输入流java.io.DataInputStream和字节数据输出流java.io.DataOutputStream,使用字节数据流进行基本的写入和读取操作时,可以不必担心不同平台之间数据大小差异的问题,因为java的数据类型占用空间的大小都是有规定的。

[请点击查看更多内容 转自博客]

» 字节对象输入、输出流

字节对象输入、输出流也就是对象序列化,对象序列化就是指将继承了serializable接口的类的实例转化成一个字节序列,并且可以将其存储在永久性存储介质中。在需要的时候可以将其从存储介质中取出并还原成具体的实例来进行使用。Java提供了字节对象输入流java.io.ObjectInputStream和字节对象输出流java.io.ObjectOutputStream来实现这样的操作。类java.io.PrintStream是非常重要的输出流类。表示标准输出并用来在控制台窗口中输出信息的System.out是java.io.PrintStream类型的变量。类java.io.PrintStream具有非常良好的特性:

  • 它包含可以用来直接输出多少种类型数据的不同成员方法
  • 它的大部分成员方法不抛出异常
  • 可以选择是否采用自动强制输出(flush)特性。如果采用自动强制输出特性,则当输出回车换行时,在缓冲中的数据一般会全部自动写入指定的文件或在控制台窗口中显示

[请点击查看更多内容 转自博客]

» 文件类

在输入输出处理中,对于文件的读写,需要使用到java.io包中有关于文件处理的类,其中包括有File、FileInputStream、FileOutputStream、RandomAccessFile和FileDescriptor;接口有FilenameFilter.

[请点击查看更多内容 转自文章]

» File 类

学习如何读取和写入数据之前,首先需要了解一个文件操作的工具类File,使用该类即可以处理数据文件也可以处理目录,并且通过该类可以列出File实例所代表的目录下所有的文件名。在处理一个目录之前,首先要做的是创建一个file的实例用于表示该目录,语法如下。

File file = new File(“src”);

在不同的操作系统中的路径名称的表示也是不同的,如在windows中,路径名为“c:Program FileJava”,而在linux中的路径就是“/home/Program File/Java”。这是因为windows中使用的是UNC路径名,以“\”开始的目录表示上下文环境所在的目录的硬盘根目录,如果没有“\”作为路径的开始,则表示相对于当前工作目录的路径,并且通过“盘符(C/D/E….):”形式表示硬盘指定。然而在Linux中,并没有硬盘驱动器的概念,所以它的路径指定必须以“/”来表示根目录开始的绝对路径,其他的则是相对路径。

[请点击查看更多内容 转自博客笔记]

» 字符流

字符输入输出流是Reader和Writer,Reader类是字符输入流的基类,其中包括有FileReader、BufferedReader等子类。Writer是字符输出流的基类。只用Reader和Writer对流数据的操作是以一个字符的长度为单位进行处理的,也可以进行字符的编码处理。

[请点击查看更多内容 转自文章]

» 字符读、写流

字符流支持Unicode标准字符集,如果进行文本读写,通常在使用Reader和Writer的子类时要重新定义相关的方法。而Reader和Writer只提供了用于字符流处理的接口,而不能生成实例,只可以通过他们的子类对象来处理字符流。

Reader是Java语言中字符输入流所有类的父类,该类是抽象类,不能被实例化。

Writer是Java语言中字符输出流所有类的父类,该类是抽象类,不能被实例化。

可以使用Reader和Writer的子类InputStreamReader和OutputStreamWriter来进行字符的处理,也就是以字符为基本单位进行读写操作,并且可以实现字符流和字节流之间的转化字符流操作比字节流操作效率要高。

使用InputStream和OutputStream对象为构造方法的参数创建InputStreamReader和OutputStreamWriter变量可以直接读取文本,而不需要自己再判断字符编码。

[请点击查看更多内容 转自博客]

» 文件读、写字符流

文件读写字符流是FileReader和FileWriter,它们分别继承自InputStreamReader与OutputStreamWriter,而且使用文件输入输出流可以直接指定文件名或者使用File来打开指定的文件。如果需要指定编码的话,就需要使用InputStreamReader和OutputStreamWriter。

Filereader中有两种形式的常用构造方法如下:

  • FileReader(File file):创建一个给定file文件的FilerReader
  • FileReader(String name):创建一个给定文件名name的FileReader

FileWriter中有四种形式的常用构造方法如下:

  • FileWriter(File file):创建一个指定文件路径的FileWriter
  • FileWriter(File file,Boolean b):创建一个指定文件路径的FileWriter,并根据Boolean值区分文件的打开方式
  • filewriter(string name):创建一个指定文件名的filewriter
  • FileWriter(String name,Boolean b)

[请点击查看更多内容 转自博客]

» 字符缓冲读、写流

字符缓冲读写流是BufferedReader和BufferedWriter,它们的默认缓冲区为8192个字符。和字节缓冲读写流类似,读取文件时,需要首先将文件读取到缓冲区,然后使用read()方法从缓冲区读取,如果缓冲区的数据不足,就会再次从文件中读取字符到缓冲区。同理BufferedWriter写入数据时,不会直接将数据写入到目的地,而是写入到缓冲区中。

BufferedReader类中有如下两个构造方法,并支持mark()和reset()方法:

  • BufferedReader(Reader in):以reader对象in为参数穿件BufferedReader
  • BufferedReader(Reader in,int sz):以reader对象in为参数创建BufferedReader,并且可以通过参数sz来指定缓冲区的大小

BufferedWriter类中有如下两个构造方法:

  • BufferedWriter(Writer out):以Writer对象out为参数创建BufferedWriter
  • BufferedWriter(Writer out,int sz):以Writer对象out为参数创建BufferedWriter,并且可以通过参数sz来指定缓冲区的大小

[请点击查看更多内容 转自博客]

 

主题二 多线程

» 线程的概念

线程是实现并发的一种有效的手段,多线程是现当代操作系统开发的发展方向。这就好比一个人在手忙脚乱的时候,总希望再分身出一个自己,或者是恨不得多长出一只手来,那多出来的“分身”或者是希望多长出来的手就相当于线程,也就是多线程。

[请点击查看更多内容 转自百度百科]

» 进程的概念

进程就是一个执行的程序任务,多进程的多任务处理特点就是允许计算机同时处理两个或两个以上的程序。线程则是比进程要小的单位,由前面介绍的进程来管理。一个多线程的程序要比多进程的程序需要更少的管理资源。

[请点击查看更多内容 转自百度百科]

» 线程与进程的关系

  • 线程是进程的一个组成部分,进程创建时一般只有一个线程,需要时可由这个线程创建其它线程
  • 一个进程可以有多个线程,这些线程共享进程的资源,在进程的空间中并发活动
  • 进程是资源分配的基本单位,资源并不分配给线程,线程基本上不拥有资源,只使用进程的资源
  • 线程是处理机调度的执行单位,即处理机是分给线程的,线程使用的资源是进程所分到的资源
  • 进程可以并发执行,线程也可以并发执行
  • 进程切换的开销远大于线程切换的开销
  • 线程也有生命期,在运行过程中也需要同步

[请点击查看更多内容 转自博客]

» 开启多线程的优点和缺点

提高界面程序响应速度。通过使用线程,可以将需要大量时间完成的流程在后台启动单独的线程完成,提高前台界面的相应速度。

充分利用系统资源,提高效率。通过在一个程序内部同时执行多个流程,可以充分利用CPU等系统资源,从而最大限度的发挥硬件的性能。

当程序中的线程数量比较多时,系统将花费大量的时间进行线程的切换,这反而会降低程序的执行效率。但是,相对于优势来说,劣势还是很有限的,所以现在的项目开发中,多线程编程技术得到了广泛的应用。

[请点击查看更多内容 转自文章]

» 线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程,这就是所谓的生命周期。一个线程在它的生命周期内有5种状态:

  • 新建(new Thread):当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)
  • 就绪(runnable):线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源
  • 运行(running):线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束
  • 死亡(dead):当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行
  • 堵塞(blocked)由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态

[请点击查看更多内容 转自博客]

» 进程和程序的区别和联系

  • 程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。反映了一个程序在一定的数据集上运行的全部动态过程
  • 进程和程序并不是一一对应的,一个程序执行在不同的数据集上就成为不同的进程,可以用进程控制块来唯一地标识每个进程。而这一点正是程序无法做到的,由于程序没有和数据产生直接的联系,既使是执行不同的数据的程序,他们的指令的集合依然是一样的,所以无法唯一地标识出这些运行于不同数据集上的程序。一般来说,一个进程肯定有一个与之对应的程序,而且只有一个。而一个程序有可能没有与之对应的进程(因为它没有执行),也有可能有多个进程与之对应(运行在几个不同的数据集上)
  • 进程还具有并发性和交往性,这也与程序的封闭性不同

[请点击查看更多内容 转自博客]

» 线程的优先级和调度

Java的每个线程都有一个优先级,当有多个线程处于就绪状态时,线程调度程序根据线程的优先级调度线程运行。

  • 抢占式调度策略
  • 时间片轮转调度策略

[请点击查看更多内容 转自博客]

» 线程状态的转变

一个线程在其生命周期中可以从一种状态改变到另一种状态

控制线程的启动和结束:

当一个新建的线程调用它的start()方法后即进入就绪状态,处于就绪状态的线程被线程调度程序选中就可以获得CPU时间,进入运行状态,该线程就开始运行run()方法。

控制线程的结束稍微复杂一点。如果线程的run()方法是一个确定次数的循环,则循环结束后,线程运行就结束了,线程对象即进入死亡状态。如果run()方法是一个不确定循环,早期的方法是调用线程对象的stop()方法,然而由于该方法可能导致线程死锁,因此从1.1版开始,不推荐使用该方法结束线程。一般是通过设置一个标志变量,在程序中改变标志变量的值实现结束线程。

线程阻塞条件:

处于运行状态的线程除了可以进入死亡状态外,还可能进入就绪状态和阻塞状态。下面分别讨论这两种情况:

  • 运行状态到就绪状态

处于运行状态的线程如果调用了yield()方法,那么它将放弃CPU时间,使当前正在运行的线程进入就绪状态。

  • 运行状态到阻塞状态

有多种原因可使当前运行的线程进入阻塞状态,进入阻塞状态的线程当相应的事件结束或条件满足时进入就绪状态。使线程进入阻塞状态可能有多种原因:

线程调用了sleep()方法,线程进入睡眠状态。

I/O阻塞。

有时要求当前线程的执行在另一个线程执行结束后再继续执行,这时可以调用join()方法实现。

线程调用了wait()方法,等待某个条件变量,此时该线程进入阻塞状态。直到被通知(调用了notify()或notifyAll()方法)结束等待后,线程回到就绪状态。

另外如果线程不能获得对象锁,也进入就绪状态。

[请点击查看更多内容 转自博客]

» Java 创建线程的两个方法

Java提供了线程类Thread来创建多线程的程序。其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象。每个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法:

  • 需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法
  • 实现Runnalbe接口,重载Runnalbe接口中的run()方法

[请点击查看更多内容 转自博客]

» Thread 类的方法

currentThread() 返回当前运行的Thread对象。

start() 启动一个线程。

run() 线程体,由start()方法调用,当run()方法返回时,当前的线程结束。

stop() 使调用它的线程立即停止执行。

sleep(int n)使线程睡眠n毫秒,n毫秒后,线程可以再次运行。

suspend() 使线程挂起,暂停运行。

resume() 恢复挂起的线程,使其处于可运行状态(Runnable)。

yield() 将CPU控制权主动移交到下一个可运行线程。

isAlive() 如果线程已被启动并且未被终止,那么isAlive()返回true。如果返回false,则该线程是新创建或是已被终止的。

[请点击查看更多内容 转自百度贴吧]

» sleep与yield的区别

  • sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行
  • sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会

[请点击查看更多内容 转自博客]

» 互斥锁

在Java中,每个对象都对应于一个可以称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。就像使用旅馆房间,如果房间没有人住,你可以住进去,同时锁上门,这样别人就不会进房间打扰你。同样道理,如果资源现在没有被使用,线程可以得到互斥锁,从而得到线程的使用权。当线程执行完毕后,它放弃互斥锁,就像你离开房间,让门开着等待下一位顾客。如果已经有一个线程已经取得互斥锁,其余的线程就必须等待当前线程放弃互斥锁,这就好比你到房门前时发现门已锁住,你必须等待房间里的人出来,打开门,你才能进入。

[请点击查看更多内容 转自博客]

» 线程同步的特点

[请点击查看更多内容 转自Java编程网]

» 线程的同步

同步是一种保证共享资源完整性的手段,具体作法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码块。

  • 一把锁可以锁住多个同步代码块
  • 锁对非同步代码块无效
  • 当一个线程进入同步代码块,并不意味着指定代码必须以不中断的方式运行
  • 当一个线程占有了某个对象的锁,其他需要获得这个锁的线程就进入锁池中,等待获得锁的机会

[请点击查看更多内容 转自百度百科]

» 线程通信

不同的线程执行不同的任务,如果这些任务有某种联系,线程之间必须能够通信,协调完成工作。例如生产者和消费者共同操作堆栈,当堆栈为空时,消费者无法取出产品,应该先通知生产者向堆栈中加入产品。当堆栈已满时,生产者无法继续加入产品,应该先通知消费者从堆栈中取出产品。

java.lang.Object类中提供了两个用于线程通信的方法:

  • wait():执行该方法的线程释放对象的锁,Java虚拟机把该线程放到该对象的等待池中。该线程等待其他线程将它唤醒
  • notify():执行该方法的线程唤醒在对象的等待池中等待的一个线程。Java虚拟机从对象的等待池中随机的选择一个线程,把它转到对象的锁池中

[请点击查看更多内容 转自博客]

 

主题三 网络编程技术

» 网络编程

网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据。程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴。在发送和接收数据时,大部分的程序设计语言都设计了专门的API实现这些功能,程序员只需要调用即可。

[请点击查看更多内容 转自博客]

» 网络通讯方式

在现有的网络中,网络通讯的方式主要有两种:

  • TCP(传输控制协议)方式
  • UDP(用户数据报协议)方式

[请点击查看更多内容 转自文章]

» Socket

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

[请点击查看更多内容 转自百度百科]

» Socket通讯的过程

Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

  • 创建Socket
  • 打开连接到Socket的输入/出流
  • 按照一定的协议对Socket进行读/写操作
  • 关闭Socket

[请点击查看更多内容 转自博客]

» ServerSocket

在服务器端使用的是ServerSocket,主要用于接收客户端传来的对象。

可以使用下面的语句获得客户端的请求:

Socket linksocket = myserver.accept();

获得连接后进行通信,然后使用下面的语句关闭服务器监听:

myserver.close();

[请点击查看更多内容 转自文章]

» DatagramPacket

在使用自寻址包之前,你需要首先熟悉DatagramPacket类,地址信息和自寻址包以字节数组的方式同时压缩入这个类创建的对象中

DatagramPacket有数个构造函数,即使这些构造函数的形式不同,但通常情况下他们都有两个共同的参数:byte [] buffer 和 int length,buffer参数包含了一个对保存自寻址数据包信息的字节数组的引用,length表示字节数组的长度。

最简单的构造函数是DatagramPacket(byte [] buffer, int length),这个构造函数确定了自寻址数据包 数组和数组的长度,但没有任何自寻址数据包的地址和端口信息,这些信息可以后面通过调用方法setAddress(InetAddress addr)和 setPort(int port)添加上。

[请点击查看更多内容 转自博客]

» URL

URL(Uniform Resource Locator)即统一资源定位符,表示在internet上的某一个具体资源的访问地址。

[请点击查看更多内容 转自百度百科]

» TCP网络程序的工作原理

TCP 客户端程序与TCP服务器程序的交互过程:

  • 服务器程序创建一个ServerSocket,然后调用accept方法等待客户来连接
  • 客户端创建一个Socket并请求与服务器建立连接
  • 服务器接收客户的连接请求,并创建一个新Socket与该客户建立专线连接
  • 建立了连接的两个Socket在一个单独的线程(服务器程序创建)上对话
  • 服务器开始等待新的连接请求,当新的连接请求到达时,重复2和5的过程

[请点击查看更多内容 转自博客]

» UDP

UDP(User Datagram Protocol) 用户数据报协议,用户数据报协议(UDP)是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。 UDP 协议基本上是 IP 协议与上层协议的接口。 UDP 协议适用端口分别运行在同一台设备上的多个应用程序。

UDP协议有如下的特点:

  • UDP传送数据前并不与对方建立连接,即UDP是无连接的,在传输数据前,发送方和接收方相互交换信息使双方同步
  • UDP不对收到的数据进行排序,在UDP报文的首部中并没有关于数据顺序的信息(如TCP所采用的序号),而且报文不一定按顺序到达的,所以接收端无从排起
  • UDP对接收到的数据报不发送确认信号,发送端不知道数据是否被正确接收,也不会重发数据
  • UDP传送数据较TCP快速,系统开销也少

从以上特点可知,UDP提供的是无连接的、不可靠的数据传送方式,是一种尽力而为的数据交付服务。

[请点击查看更多内容 转自百度百科]

» DatagramSocket类

DatagramSocket本身只是码头,不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的。

[请点击查看更多内容 转自百度空间]

» InetAddress类

InetAddress类在网络API套接字编程中扮演了一个重要角色。参数传递给流套接字类和自寻址套接字类构造器或非构造器方法。InetAddress描述了32位或64位IP地址,要完成这个功能,InetAddress类主要依靠两个支持类Inet4Address 和 Inet6Address,这三个类是继承关系,InetAddrress是父类,Inet4Address 和 Inet6Address是子类。由于InetAddress类只有一个构造函数,而且不能传递参数,所以不能直接创建InetAddress对象。

[请点击查看更多内容 转自博客]

原文地址:https://www.cnblogs.com/webapplee/p/3850390.html