Java线程和I/O总结


1 JAVAJAVAJAVA JAVA 中 的线程和中的线程和中的线程和 中的线程和 I/OI/OI/O I/O 一、线程 111 1 、基本原理:  进程是运行中的应用程序,是一套顺序执行的指令,每个进程都有自己独立的内存空间 。  线程是一个并发执行的顺序流,一个进程包括多个顺序执行流程,这些流程称为线程。  JVM 就是一个进程,在 JVM 中分出线程,比如 main()就是主线程。同时开辟并发执行 的序列即多线程。 调用 start()方法,线程启动;run()方法结束,线程就终止。  线程是一个操作系统创建并维护的一个资源。对于单 CPU 系统来说,某一个时刻只可 能由一个线程在运行。一个 Thread 对象就表示一个线程。 线程由三部分组成:  进程的数据空间独立;线程的数据空间共享, 能充分使用 CPU 的资源。  线程间通信更容易。共享数据就要加锁、解锁,会降低效率。 进程的调度: 进程的调度是由 OS 负责的(有的系统为独占式,有的系统为共享式,根据重要性,进 程有优先级)。由 OS 将时间分为若干个时间片,调度系统把每个时间片分给多个进程,而 线程也是类似的。JAVA 在语言级支持多线程。负责分配时间的仍然是 OS。性能调优是根 据时间片划分,时间片会影响整个操作系统。 为了让某些工作并行,在 JVM 进程的主线程中开辟多个线程,线程并发执行是一个宏 观概念,微观上是串行的。这就是线程的机制。 222 2 、启动线程的两种方式: 创建线程步骤:首先调用构造方法构造对象,在构造方法中调用 start()方法来配置线程 , 然后由线程执行机制调用 run()。如果不调用 start(),线程将永远不会启动。 对于线程的启动有两种方式: ① CPU 分配给线程的时间片 ② 线程代码(写在 run 方法中) ③ 线程数据 进程 代码 线程 指的是堆空间共享,而栈空间独立 CPUCPUCPU CPU 数据 数 据代码 代码 CPUCPUCPU CPU CPUCPUCPU CPU Thread1Thread1Thread1 Thread1 Thread2Thread2Thread2 Thread2 ① CPU(何时做) ② 代码 (做什么) ③ 数据 (对谁做) 2 (111 1 )、继承 ThreadThreadThread Thread ,覆盖 run()run()run() run() 方法,调用 start()start()start() start() 启动; 如: (222 2 )、实现 RunnableRunnableRunnable Runnable 接口,用来构造线程对象。(线程对象不是线程,但代表一个线程) 如: 333 3 、线程的生命周期:(线程七状态图)☆ ccc c lasslasslass lass ThreadAThreadAThreadA ThreadA extendsextendsextends extends Thread{Thread{Thread{ Thread{ ppp p ublicublicublic ublic voidvoidvoid void r un(){run(){run(){ run(){ //run 方法必须为 public void ……… … ////// // 线 程在执行时运行的代码 }}} } }}} } classclassclass class MyRunnableMyRunnableMyRunnable MyRunnable implementsimplementsimplements implements Runnable{Runnable{Runnable{ Runnable{ publicpublicpublic public voidvoidvoid void r un(){run(){run(){ run(){ ……… … ////// // 线 程在执行时运行的代码 }}} } }}} } publicpublicpublic public classclassclass class TestThread{TestThread{TestThread{ TestThread{ pulbicpulbicpulbic pulbic staticstaticstatic static voidvoidvoid void m ain(main(main( main( String[]String[]String[] String[] argsargsargs args ){){){ ){ ////// // 只有 等到所有的线程全部结束之后,主线程才退出。 ThreadThreadThread Thread t1=newt1=newt1=new t1=new TTT T hreadhreadhread hread AAA A ();();(); (); ttt t 1.start();1.start();1.start(); 1.start(); //一个线程只可启动一次,否则抛 IllegalThreadStateException }}} } }}} } publicpublicpublic public classclassclass class TestThread{TestThread{TestThread{ TestThread{ ////// // 接着 来创建一个线程类对象 publicpublicpublic public staticstaticstatic static voidvoidvoid void m ain(String[]main(String[]main(String[] main(String[] args){args){args){ args){ ThreadThreadThread Thread t1t1t1 t1 === = n ewnewnew new ThreadA();ThreadA();ThreadA(); ThreadA(); //创建一个 t1 线程 RunnableRunnableRunnable Runnable rrr r === = newnewnew new MyRunnable();MyRunnable();MyRunnable(); MyRunnable(); //创建一个 Runnable 的实例 r ThreadThreadThread Thread t2t2t2 t2 === = n ewnewnew new Thread(r);Thread(r);Thread(r); Thread(r); //使用一个 Runnable 的实例来创建一个线程 t2 ttt t 111 1 .start();.start();.start(); .start(); t2.start();t2.start();t2.start(); t2.start(); }}} } }}} } 111 1 、 222 2 、调用 yield()yield()yield() yield() t2t2t2 t2 进入可运行状态t1t1t1 t1 t2t2t2 t2 111 1 、数据输入结束; 222 2 、sleepsleepsleep sleep 时间到; 333 3 、 ((( ( 得到了锁))) ) this.wait()this.wait()this.wait() this.wait() 让当前正在执行 的线程到 thisthisthis this 对象的等待 池中等待;并且同时释放 thisthisthis this 对象的锁。必须出现在 synchronizedsynchronizedsynchronized synchronized 代码块内,且 对象必须与 synchronizedsynchronizedsynchronized synchronized 加 锁的对象相同。 this.notify()this.notify()this.notify() this.notify() 或 this.notifyAllthis.notifyAllthis.notifyAll this.notifyAll ()()() () 进入 thisthisthis this 对象的锁池,等待 thisthisthis this 对象的锁 111 1 、等待数据输入 222 2 、sleep()sleep()sleep() sleep() 333 3 、t2t2t2 t2 调用 t1.joint1.joint1.join t1.join ttt t 222 2 阻塞 111 1 、 222 2 、异常中止 3 444 4 、实际开发中的关注点 ☆ 创建几个线程操作比较简单,而在实际开发中,我们真正要关心的是被线程共享的数据, 主要看 synchronizedsynchronizedsynchronized synchronized 加在何处。往往不加在线程里面,而是在共享的对象上。 多线程出现故障的原因: A. 两个线程同时访问一个数据资源(该资源称为临界资源),造成数据不一致和不完整 。 B. 数据的不一致往往是因为一个线程中的两个关联的操作只完成了一步。 避免以上的问题可采用对数据进行加锁的方法: 线程的同步 用法一: synchronizedsynchronizedsynchronized synchronized (o){(o){(o){ (o){ 原子操作代码块 }}} } 用法二: publicpublicpublic public synchronizedsynchronizedsynchronized synchronized voidvoidvoid void mmm m e thodethodethod ethod ()()() () {{{ { ……… … }}} } 等价于对当前对象加锁: 加锁的原则: ① synchronized 所包括的代码要尽可能少,也就是将某些不可分割的操作设置为原子操作。 ② 必要时把 this 换成 static Object 成员,可以保证是对同一个对象加锁。 ③ 要避免线程的死锁。 加锁时注意:  构造方法 不能加锁,因为还没有当前对象;  抽象方法 也不能加锁,因为子类覆盖方法后可以不加 synchronized,父类就有多余的话 。  静态方法可以加锁,而这时是对其类对象加锁。 每一个对象都有一个锁池(lock pool),装的是等待该对象锁标记的线程。 补充:通过 synchronized,可知 Vector 与 ArrayList 的区别就是 Vector 所有的方法都加有 synchronized。所以 Vector 更为安全。但效率也低,考虑并发时也不考虑用 Vector。 同样:Hashtable 比较 HashMap 也是如此。 另外,一个线程可以同时拥有多个对象的锁标记 synchronized(o1){synchronized(o1){synchronized(o1){ synchronized(o1){ //同步代码块是可以嵌套的 synchronized(o2){synchronized(o2){synchronized(o2){ synchronized(o2){ }}} } }}} } publicpublicpublic public voidvoidvoid void m ethod(){method(){method(){ method(){ synchronizedsynchronizedsynchronized synchronized (this){...}(this){...}(this){...} (this){...} }}} } //在整个方法中,对 this 加锁。 哪个线程能拿到 o 的锁标记,哪个线程才能进入这个同步代码块, 等到出了代码块后才释放锁标记。 未加同步可能造成数据不一致和数据不完整的缺陷。(同步代码块) 4 当一个线程阻塞在 A 对象的锁池里的时候,不会释放其所拥有的其他对象的锁标记。 线程的优先级: 值越大优先级越高,优先级越高被 OS 选中的可能性就越大。(不建议使用,因为不同 操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。 设置线程优先级:setPriority(Thread. MAX_PRIORITY); 注:程序的跨平台性:除了能够运行,还必须保证运行的结果。 555 5 、死锁 死锁:多线程中每个线程不释放自己拥有的资源,却申请别的线程的资源,造成死锁。 解决方式 —— 线程间通信: 线程 t1: o.wait()o.wait()o.wait() o.wait() //让当前运行的线程等待。 前提:必须在对 o 加锁的同步代码块里 1. t1 会释放其所拥有的所有锁标记 2. t1 会进入 o 的等待队列 线程 t2: o.notify()o.notify()o.notify() o.notify() / o.notifyAll()o.notifyAll()o.notifyAll() o.notifyAll() 前提:必须在对 o 加锁的同步代码块里; t2 会从 o 的等待队列中释放一个线程/所有线程。 推荐:尽量用 notifyAll 取代 notify, 因为若用 notify 是由 OS 决定释放哪一个线程。 一些规律: a. 只有运行状态的线程才有机会执行代码! b. 只有等到所有线程终止,进程才结束! c. 当一个对象分配给某线程锁标记时,其它线程不能访问同步方法,但能访问非同步方法! d. 每一个对象都有一个互斥锁标记(monitor),这个锁标记是准备分配给线程的,且只能分 配给一个线程。 wait()wait()wait() wait() 和 sleep()sleep()sleep() sleep() 的联系和区别: 1. wait()是从 Object 继承下来的方法,而 sleep()是 Thread 中的静态方法。 2. wait() 和 sleep()都要阻塞运行,释放 CPU 资源。 3. wait()要释放锁; 而 sleep()不释放锁。 4. wait()方法只能用在一个同步代码块内;而 sleep()无此约束。 ☆ 小技巧:必要时可利用 Object 中的 互斥锁标记(monitor)(monitor)(monitor) (monitor) 、锁池(lock(lock(lock (lock pool)pool)pool) pool) 、等待池(w(w(w (w ait aitait ait pool)pool)pool) pool) 。 用其 wait() / notify() 方法,自己制造临界资源来进行线程间通讯。 以下附带一些主要方法说明: sleep()sleep()sleep() sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行, 5 但它并不释放对象锁。也就是如果有 synchronized 同步块,其他线程仍然不能访问共享数据 。 注意该方法要捕获异常。 比如有两个线程同时执行(没有 synchronized),一个线程优先级为 MAX_PRIORITY,另 一个为 MIN_PRIORITY,如果没有 sleep()方法,只能高优先级的线程执行完成后,低优先 级的线程才能执行;但如果当高优先级的线程 sleep(5000)后,低优先级就有机会执行了。 join()join()join() join() 当前运行的线程可以调用另一个线程的 join()方法,当前运行的线程将转入阻塞状态, 直到另一个线程运行完毕,它才进入可运行状态。注意该方法也要捕获异常。 yield()yield()yield() yield() 当一个线程对象调用 yield()方法时会马上交出执行权,回到可运行状态,等待 OS 的再 次调用。 wait()wait()wait() wait() 和 notify()notify()notify() notify() 、notifyAll()notifyAll()notifyAll() notifyAll() 这三个方法用于协调多个线程对共享数据的存取,所以必须在 synchronized 语句块内使 用这三个方法。synchronized 这个关键字用于保护共享数据,阻止其他线程对共享数据的存 取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出 synchronized 数据块 时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。 wait()方法使当前线程暂停执行并释放对象锁标记,让其他线程可以进入 synchronized 数据块,当前线程被放入对象等待池中。当调用 notify()方法后,将从对象的等待池中移走 一个任意的线程并放到锁池中,只有锁池中的线程能够获取锁标记并进入可运行态;如果锁 池中没有线程,则 notify()不起作用。 notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。 注意 这三个方法都是 java.lang.Ojbect 的方法! setDeamonsetDeamonsetDeamon setDeamon ((( ( truetruetrue true ))) ) 设置线程为后台运行 666 6 .JDK5.0JDK5.0JDK5.0 JDK5.0 多线程 新的并发包 java.util.concurrentjava.util.concurrentjava.util.concurrent java.util.concurrent 实现了线程与业务的分离! 让线程资源不断地重用。 池化的思想 eg.字符串池 数据库连接池 线程池 EJB 的 Bean 池 让线程重用; 让程序员从线程控制中解脱出来(专注于线程要干些什么) 自此,Thread 类不用了 代码 拿出 线程池: 一次性创建多个线程,让 其空转。若有多余的加 入,则要先等待着。 CPUCPUCPU CPU 数据 加入 6 接口: Excutor 执行器 ExcutorService Excutors 工具类 一次执行完后,线程执行不会销毁, 除非 es.shutdown(); 用线程池可以控制并发的数量(看池中参数为几) ExcutorService es = ExcutorService.newFixedThreadPool(2); //生成一个固定大小的线程池 Runnable r1 = new Code1(); Runnable r2 = new Code2(); Runnable r3 = new Code3(); es.submit(r1); es.submit(r2); es.submit(r3); //es.shutdown() Class Code1 implements Runnable{ … } ------------------------------------------------------------- CallableCallableCallable Callable 和 FutureFutureFuture Future 接口 RRR R unableunableunable unable 接口有缺陷::: : ①run() 无法抛出异常; 发现有异常无权汇报,只能自己解决 ②无返回值 void run(); 无返回值不会将结果返回 用新的接口 CallableCallableCallable Callable 替换掉 Runable。 call() 取代 run() Interface Callable 由用户决定(大仁大意) V call() throws Exception 想返回什么类型都行,而且还能抛出异常。 就在这两点上取代了 Runnable ThreadThreadThread Thread   ExcatorExcatorExcator Excator RRR R unableunableunable unable   CallableCallableCallable Callable 接口 +++ + FatureFatureFature Fature 接口 Fature 与 Callable 配合使用 可以拿到 call()返回值 用 submit 提交 调 get()方法 取得返回值 ---------------------------------------------------------------- LockLockLock Lock 接口 我们可以用 Lock 对象,来对临界资源加锁,只有获得 Lock 对象才能访问临界资源,如果 没有获得 Lock 对象,就会进入 Lock 对象的锁池。tryLock()方法会返回布尔值,这个方法是 用来判断这个锁对象是不是已经被线程获取,如果返回值为 true,则会直接获得这个锁对象 , 如果返回 false,线程不会阻塞还会继续运行。 以下示例: Lock lock=new ReentrantLock(); publci void test(){ 7 try{ lock.lock(); //加锁 ..... //需要加锁的临界资源 }finally{ lock.unlock(); //解锁。 } } 针对 SynchonizeSynchonizeSynchonize Synchonize : 5.0 中锁也是一个对象 在 JDK5.0 中, 锁与任何对象无关: Lock 接口的实现类的对象就是锁对象 LockLockLock Lock locklocklock lock === = newnewnew new ReentrantLock();ReentrantLock();ReentrantLock(); ReentrantLock(); lock.lock();lock.lock();lock.lock(); lock.lock(); //钥匙掌握在自己手上了 如果再次调用 lock(),将在该锁的等待队列中等待 … lock.unlock();lock.unlock();lock.unlock(); lock.unlock(); //必须放在 finally 里 //最终锁必须打开 tryLock()tryLock()tryLock() tryLock() //先看看能拿到锁否 返回 boolean 尝试获得锁,避免拿不到而阻塞 提高了效率! 返回 true 表示能获得,同时也已经获得了。这样提高了系统的效率。 SynchonizeSynchonizeSynchonize Synchonize 缺点: 若全读时,应非同步; 若一写多读,则应把读方法加锁。 应多拿几把锁: 共享锁---多个人 独占锁---只分给一个人 ①读锁 若正在读, 可分配读锁,不可分配写锁。 ②写锁 若写锁分配后,读锁不允许分配 拿两把锁就可以控制 ReadWriteLockReadWriteLockReadWriteLock ReadWriteLock 读写锁 具备两把锁 方法: Lock readLock() Lock writeLock() //更好地在效率与安全之间找到平衡点 ConditionConditionCondition Condition 类 其对象就是等待队列对象 Lock 中有 newCondition()方法获得 Condition 对象 await()await()await() await() <-><-><-> <-> wait() 类似于 signal()signal()signal() signal() <-><-><-> <-> notify() 线程分组,“救人好救”,分类释放 ---------------------------------------------------------------------- 8 QueueQueueQueue Queue 接口(Collection 的子接口,对列接口) LinkedList 也实现了这个在 JDK5.0 中的新接口 Queue,并且这个类自动的实现了生产者和 消费者的同步。 ---------------------------------------------------------------------- JDK5.0JDK5.0JDK5.0 JDK5.0 的高级同步 Semaphore 类(信号量)也就是可以向线程分配许可证,指定许可证数量可以实现多线程的 同步。 Semaphore s=new Semaphore(5);//可以分配 5 个许可证,许可证都被分配出去时,得不到许 可证的线程就会阻塞。 acquire()方法,获得许可证。release() 方法,释放一个许可证,也有相应的方法指定释放和 获得许可证的数量的方法。 CountDownLatch 类: CountDownLatch 中有个计数器,访问这个类的对象就会从计数器中减一,countDown()方法 会将原有的设置的计数器值减一,当 countdown 计数器为零时会释放所有 await()的线程。 CyclicBarrier 类: CyclicBarrier 和 CountDownLatch 比较相似 CyclicBarrier 在构造时给出线程数,只有等待的线程数到了构造方法中指定的数量,当最后 一个线程等待后,所有的线程都会被释放,这个类是一个多线程汇合的工具。 Exchanger 类:用 exchange()方法可以使两个线程间相互交换对象,在两线程的同步点,等 待第二个线程。在同步点时,交换对象,并同时被释放。 ----------------------------------- 纵览三个新加的多线程包: Java5.0 里新加入了三个多线程包: java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks ①java.util.concurrent 包含了常用的多线程工具,是新的多线程工具的主体。 ②java.util.concurrent.atomic 包含了不用加锁情况下就能改变值的原子变量,比如说 AtomicInteger 提供了 addAndGet()方法。Add 和 Get 是两个不同的操作,为了保证别的线程 不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用 AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子 量级发生的,不会被别的线程干扰。 ③java.util.concurrent.locks 包包含锁定的工具。 9 二、I/OI/OI/O I/O 111 1 、I/OI/OI/O I/O :是在 JVM 和外部数据源之间交换数据。 222 2 、流的分类: (1) 按数据传输方向划分:输入流和输出流 <针对 JVM 而言> I 输入流 JVM <—— 数据源 InputXXX O 输出流 JVM——> 数据源 OutputXXX (2) 按数据单位划分:字节流和字符流 字节流 一次传输一个字节; 字符流 一次传输一个字符。 字节流类: 抽象父类: InputStream 字节输入流 xxxInputStream 实现类: BufferedInputStream 缓冲流——过滤流 ByteArrayInputStream 字节数组流——节点流 DataInputStream 处理 Java 标准数据流——过滤流 FileInputStream 处理文件 IO 流——节点流 FilterInputStream 实现过滤流——字节过滤流的父类 PipedInputStream 管道流,在两个线程交换数据 PrintStream 包含 print() 和 println(),很实用 RandomAccessFile 只支持随机访问文件,用于定位的 seek()方法 SequenceInputStream 在合并两个文件时较好用(顺序流) 字符流: 可以解决字符编码问题 ReaderReaderReader Reader 和 WriterWriterWriter Writer (字符流类,所有字符流的父类型) (3)按流的功能划分: 节点流 / 过滤流(使用装饰模式) 节点流用来传输数据。 过滤流用来封装节点流或者其他过滤流,从而给节点流或其他的过滤流增加功能。 首先要通过构造得到 InputStream 或 OutputStream 对象 关闭流时只需关最外层的流。 注意使用 out.flush()来清空缓冲区。 而 out.close()默认调用 flush I/O 本身是抽象的,当 流、源和目的确定了 I/O 就不抽象了。 JVMJVMJVM JVM FileFileFile File /// / DBDBDB DB /// / N etworkNetworkNetwork Network ……… … 输入 输出 10   构造方法的记忆方法: (1)、代表源的事物,构造方法就加源 例如 FileInputStream File 代表源的事物,构造方法就加源 public FileInputStream(File file) throws FileNotFoundException (2)、代表功能,构造方法就放 InputStreamInputStreamInputStream InputStream 或 OutputStreamOutputStreamOutputStream OutputStream 例如 ObjectInputStream Object 代表功能,构造方法就放 InputStream DataInputStream Data 分为 int、byte、char 等 8 种简单类型外加 String, 构造方法放 InputStream PushbackInputStream Pushback代表具有缓冲的功能,所以构造方法放InputStream. InputStream, OutputStream 为抽象的,子类型不抽象 建议主要记 Read / Write 方法即可,其它的掌握规律 比如看到 Data 就要想到会装不同类型数据: int、byte、char … 333 3 、JavaJavaJava Java I/OI/OI/O I/O 类层级图: InputStreamInputStreamInputStream InputStream 类 所有字节输入流的父类,如:FileInputStream,ObjectInputStream,PipedInputStrean 三个基本的 read()方法 A. int read(): 从流里读出的一个字节或者返回-1; B. int read(byte[]):将数据读入到字节数组中,并返回所读的字节数; C. int read(byte[], int p, int len): int 参数 p 表示哪个位置,len 表示希望的长度。 FileInputStream 类 File:代表了磁盘上的一个文件或者目录 lujy@tarena.com.cn 11 DataInputStream 类 PipedInputStream 类 管道流用于线程之间交换数据 BuferOutputStream 给流增加一个缓冲的功能,以空间换取时间。 在 JVM 内部开辟一块缓冲区,提高了读写的效率 利用 flush()方法,清空缓冲区。IO 编程常犯错误之一,即忘记清空缓冲区。 ByteArrayInputStream / ByteArrayOutputStream 用于以 IO 流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或者内存 映象文件的功能。 ByteArrayInputStream 的两个构造方法: ByteArrayInputStream(byte[] buf) //以字节数组做为数据源 ByteArrayInputStream(byte[] buf, int offset, int length) //从字节数组中的 offset 位置开 始的长度为 length 的字节做为数据源 ByteArrayOutputStream 的两个构造方法: //下面两种方法创建的缓冲区在数据过多时, 会自动增长 ByteArrayOutputStream() //创建一个有 32 个字节的缓冲区 ByteArrayOutputStream(int) //创建指定大小(int)的缓冲区 我们需要在内存中使用虚拟内存内容时,就可以使用 ByteArrayInputStream 和 ByteArrayOutputStream 来进行操作。 ☆ 总结规律:根据数据类型选择输入/输出流: ① byte、byte[] InputStream / OutputStream ② int、byte、char DataInputStream / DataOutStream ③ char、String Reader / Writer ④ Object ObjectInputStream / ObjectOutputStream 若考虑性能会在前面试着加 Buffered, 在①、③上加; ②则在①的基础上加。 如 FileOutputStream fo=new FileOutputStream(“a.txt”); BufferedOutputStream out=new BufferedOutputStream(fo); DataOutputStream dout=new DataOutStream(out); 字符流: 目的:更好的解决字符编解码问题。 Reader/Writer FileReader / FileWriter 节点流 每个国家制定编码方式,每个字符对应一个整数,叫做这个字符的编码,编码方式和解 码方式不统一,则会造成乱码问题。字符流以字符为数据单位,可以解决编解码问题。 ASCII 一个字符-----1B 任何一种编码均兼容 A  65 ISO8855-1 (西欧) 一个字符-----1B JVMJVMJVM JVM FileFileFile File /DB/DB/DB /DB /// / ……… … 缓冲区 lujy@tarena.com.cn 12 GB-2312 / GBK 一个字符-----2B Unicode 一个字符-----2B 会增加网络负担 Java 中的 char 是 Unicode UTF-8 变长字节(变长的 Unicode 方式) 英文-----1B 中文-----3B 这里的重点: 字节流——>>> > 字符流 InputStreamReader 类 OutputStreamWriter 类 FileInputSream fi=new FileInputStream(“2.txt”); InputStreamReader ir=new InputStreamReader(fi, ”GBK”); //只能在这时指定编码方式 BufferedReader in=new BufferedReader(ir); String s = null; While((s=in.readLine()) != null){ //readLine()阻塞方法,到换行符为止 System.out.println(s); } in.close(); Java EE 中用得较多的还是 Reader 和 Writer BufferedReader 带缓冲的字符输入流 有个 readLine()方法,遇到 null 读完。 PrintWriter 带缓冲的字符输出流,可以省略桥转换,提供了更多的方法,例如一系列 的 println(...),不抛异常 注意:流是自己开的,要自己关;不是自己创建的,不能自己关,比如 System.out 流不能关 。 对象序列化 (重点) ① 概念:把对象直接在 I/O 流上传输,就叫做对象序列化。 即用 I/O 流读写对象。 对象序列化是 RMI 的基础,RMI 又是 EJB 的基础。 ② 我们学会了如何用 IO 流传输 8 种基本类型和 String,但是要是以对象方式读出来,怎么 办? —— 保存对象主要保存对象的属性。 用 I/O 流读写对象的基本办法: ObjectInputStream / ObjectOutputStream 这一对流也是过滤流,也要先建立一个节点流。 此流具备 DataXXX 和 BufferedXXX 流的功能; 最关键的是具有:readObject() / writeObject() 直接读写对象。 ③ 注意:只有实现了 java.io.java.io.java.io. java.io. SerializableSerializableSerializable Serializable 接口的类,才可以在网络上传输。 Serializable 接口 为标记接口(没有方法的接口)。 13 序列化一个对象就是序列化此对象的所有属性。也就是这个对象的属性也要实现 Serializable 接口,依此类推。 Java 的序列化有它的一套规范,每个字节都有其含义。 Java 中 有 一 个 命 令 , serialveserialveserialve serialve 用来判断一个类是否实现 Serializable 接 口 , 会 出 现 serialVersionUID —— 对象的序列号 static final long serialVersionUID = .... 临时属性 用 transienttransienttransient transient 修饰一个属性叫做此对象的临时属性,即此属性不会参与序列化。 ④ 另外: (1).对象输入流会以 EOFException 的方式通知用户文件结束,我们可以捕获这个异常来控 制循环退出。 EOFException 异常用来做流程控制: try{ while(true){ System.out.println(in.readObject()); } } catch(EOFException e){ //e.printStackTrace(); } (2).StreamCorruptedException 表示流被打断了 对象序列化有规范,如果一个文件中已经有了一个对象,如果希望把一个对象追加进去, 则应该把原有的对象先读进来,然后连同那个新对象一起再写回去。 小技巧:1.1.1. 1. 字符串的拆分 java.util 包中提供了 StringTokenizerStringTokenizerStringTokenizer StringTokenizer 类。根据分割符拆分字符串 其中两个方法: ① public boolean hasMoreElementshasMoreElementshasMoreElements hasMoreElements () //当作循环条件 类似于迭代器的用法 ② public Object nextElementnextElementnextElement nextElement () //取元素 注意:构造参数中若 ”e:” 表示即以 e 也以: 作为分隔符
还剩12页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 3 人已下载

下载pdf

pdf贡献者

riverlover

贡献于2012-11-03

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf