多线程。线程是进程中的一个执行单元(执行路径),可以并发。
System.gc();申请启动垃圾回收器,垃圾回收器比较慢,可能会在虚拟机结束后出现。
JVM启动时就启动了多个线程,至少有两个线程可以出来:
1,执行main;2,负责垃圾回收的线程。
第一种创建线程方法:继承Thread类,覆盖run方法,调用start()启动线程;可以通过Thread类的getName()或Thread.currentThread().getName()获取线程编号(从0开始)
其他方法有wait(); notify(); sleep(time); stop();
状态:
冻结状态:由sleep(time);和wait();进入,notify();唤醒,醒后进入临时阻塞状态。连个都不具备。
临时阻塞状态:具备执行资格,但不具备执行权。
第二种创建线程方法(常用):Runnable接口,用来帮助有父类的类创建线程。通过Thread类创建对象,并将Runnable接口的子类对象作为构造函数的参数进行传递。
Thread类设计思想:带一个Runnable()接口,运行这个接口的方法体,或者运行用户自定义的run()方法,或者运行用户自定义的Runnable子类里的run()方法。这处讲解在视频13-10!
如果仅仅使用run();那最好只用Runnable来实现,它的作用就是封装线程任务。实现Runnable接口的好处:
1,将线程任务封装成对象,面向对象的思想
2,避免单继承的局限性。同步代码块 同步函数 和 静态同步函数:
synchronized(obj){ } obj是任意对象可以用object类String类等。 synchronized 修饰一个函数。
同步函数的锁是固定的this,也就是当前对象,
同步代码块的锁是任意对象。
静态同步函数用的锁是该类字节码文件对象用this.getclass() 或 类名.class获取。
建议使用同步代码块!!
同步的好处是:解决线程的安全问题。
同步的坏处是:相对降低了效率,同步外的线程每次都会判断同步锁。
必须保证 同步中每个线程用的都是同一个锁。别用局部变量。
sleep(time);有异常要抛 interruptedExceptionThread(同一个对象)多次.start会再运行一次对象的run(),对象的内部变量不会初始化。
字节码文件所属对象。JVM把所有类都封装成对象,用对象.getclass() 或者 类名.class 调用这个属性来获得该对象,在用到静态同步函数的锁会碰到。
多线程通信。多个线程在处理同一个资源,但是任务却不同,可能一个写,一个读。
1.创建资源。2.创建任务。3.创建线程,执行任务。4.开启线程。如何实现对象之间共享数据。除了静态,单例,还可以用传参的方式,就是用一个私有成员某类变量,在构造函数中传入一个对象初始化这个成员变量。
等待和唤醒机制。涉及的方法:
1、wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中,其中有InterruptedException异常要捕捉。
2、notify(): 唤醒线程池中的一个线程(任意)。
3、 notifyAll(): 唤醒线程池中的所有线程,线程池是按照锁区分的。
注意:这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是那个锁上的线程。也就是说r.wait() 和 r.notify() 操作的是r锁(监视器)对应线程池中的线程。
之所以上面这些方法定义在Object类中,因为这些都是监视器的方法,监视器其实是锁,锁可以是任意对象。
把同步代码块放在共享资源类里,比如输入和输出都是。
4、注意:使用wait(), notify(), synchronized(lock)来管理3个以上线程时候,容易造成死锁和安全问题。比如随机notify一直是那几个生产者。解决:可以用notifyAll()和无限循环判断flag来解决这个安全问题。具体来说,while(flag)解决了线程取得执行权后判断该不该执行。用while()判断标记安全!!!final变量的初始化:引用方法见此
分静态和非静态final类型变量,要么直接初始化,要么构造函数初始化,要么构造代码块初始化。同步的显示锁方式Lock。在java.util.concurrent.locks这个包里,用try{}finally{}方式锁上和解锁,lock(),unlock(); Lock lock = new Lock();
创建锁上的监视器对象。通过已有锁获取,Condition con_1 = lock.newCondition();
所以手法可以,同种线程共用一个监视器。
方法有:awai();signal();signalAll();
总结:
1、Lock借口:出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成显式操作,而且更灵活,可以一个锁上加多组监视器。
lock()获取锁。unlock()释放锁,定义在finally代码快中。
2、conditon接口:出现替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。可以让任意锁进行组合。
方法有:await();signal();signalAll();
3、其他: (1). wait 和 sleep 区别在于 wait可以制定时间也可以不制定时间。sleep必须制定时间。
(2). 在同步中,wait: 释放执行器,释放锁。sleep: 释放执行权,不释放锁。停止线程:
1、 stop方法,但已过时,不安全。
2、run方法结束,不能控。可顶一个标志位while(flag);和public setFlag(){} 把flag置false,退出循环结束任务。但是如果线程死锁或冻结wait()状态,就无法读取标志位退出。如何解决?
3、可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。。但是强制终端等待动作会发生InterruptedException,记得要处理。线程其他方法。
1、Daemon线程。t.setDaemon();设为守护线程,后台运行,必须在启动前调用,当全是后台线程时虚拟机退出。
2、join()方法。t1.join();调用这条语句的线程,必须要等t1线程先运行完终止后才能接着运行,其他不用等。
3、线程.toString()。会显示线程名 优先级 线程组。优先级可以通过.setPriority()设置,最大10最小1越大优先级越高,最高用常量来设置。线程组,可以通过对组进行操作,比如对整个组进行中断处理。
4、Thread.yield()方法。用来暂时释放执行权。
5、抽象的类一定要用abstract修饰,比如定义一个实现某接口的类,如果没有覆盖住,那些方法还是抽象的,那么这个类必须要用abstract修饰,否则报错。eclipse快捷键1
eclipse快捷键2字符串。字符串是不可变的。
new String()还不如直接用""就好;
常用的split();涉及到正则,重点掌握;
trim()重点掌握;
concat()拼接字符串,代替使用“” + “”;
charAt代替使用[];
String.valueOf()可以将基本数据类型转换为字符串形式;
equalsIgnoreCase()忽略大小写比较两字符串;
intern()不知道具体什么用;
length是所有数组都有的属性;
GB2312是中文码表。**最高位是1,所以显示都是负数。
F3显示源代码,cmd + 鼠标也可以进入声明等内容,按cmd + [返回
cmd + shift + f代码格式化StringBuffer字符串缓冲区。用于存储数据的容器。
特点:
1, 长度可变。
2, 可以存储不同类型的数据。
3, 最终要转成字符串进行使用
4, 可以对字符串进行修改。字符串本身不能,通过缓冲区可以改。
功能:
1, 添加:
StringBuffer append(); StringBuffer Insert(index, data);
2, 删除:
StringBuffer delete(start, end); 包含头,不包含尾
StringBuffer deleteCharAt(int index);删除指定位置的元素
3, 查找:
char charAt(index);
int indexOf(string);
int lastIndexOf(string);
4, 修改:
StringBuffer replace(start, end, string);先去掉那部分,再放入string
void setCharAt(index, char);StringBuilder与StringBuffer用法兼容。不同点在于前者不保证同步,多线程里不安全,后者保证同步和多线程安全。前者是JDK1.5才出现,后者JDK1.0就出现了。前者优点是速度快 用于单线程,后者加了同步锁比较慢 用于多线程。
基本数据类型对象包装类。比如有Integer, Byte, Character, Boolean等。
1, 取某数据类型的最大最小值(.MAX_VALUE),
2, 还常用在与其他类型字符串形式之间转换(.toBinaryString()),
基本数值类型+"",String类中的valueOf(基本数值类型)
3, 还常用到将字符串转换成int型整数 parseInt(string,radix) 或其他类型。 用包装类中的方法 中的 xxx parseXxx("xxx类型的字符串")只有character没有parse方法它不需要。
4, 用对象.intValue()转成相应数值
5, 进制转换 十进制->其他进制 .toString(data,radix);.toBinaryString(data); .toOctalString(data); .toHexString(data);
其他进制->十进制 parseInt(string, radix); 这个最常用!Integer对象包装类。这个类最常用,很多实用方法比如进制转换。
JDK更新后简化了书写:Integer i = 4; 自动装箱,如果装箱的是同样的一个字节,那该数据会被共享,不会重开空间。比如Integer i = 4;Integer j = 4;判断i ==j返回的是True,注意,只有一个字节是这样的,多于一个就不是这样。
i = i+4;(i.intValue() + 4); 自动拆箱 万一对象为null,所以要判断过。集合框架。存储多个对象,其特点是:
1,是用于存储对象的容器;
2,集合的长度是可变的,比如remove();
3,集合不可以存储基本数据类型。
4,那么对象是如何在集合中存储的?
因为集合容器内部的数据结构不同,有多种具体容器,不断向上抽取,就形成了集合框架。框架顶层是Collection接口 定义了多种容器常用方法:
增删改查,
用迭代器取对象元素,Iterator iterator(); 用上for循环。迭代器接口在容器内部类中实现,直接取得容器元素,让每个容器取出方式变通用,降低了容器与取出方式之间的耦合性,在多种容器中使用迭代器变得很方便,iterator接口就是所有的Collection容器进行元素取出的公共接口。
所以一个事物直接在访问另一个事物的成员,这用内部类来完成。
取交集保留 retainAll(coll),与removeAll]正相反。常用Collection接口:List, Set,Map
1,List集合:有序(存入和取出顺序一致),元素都有索引,可重复
2,List特有常见方法:有一个共性特点是都可以操作索引,按位置操作容器内容。
ArrayList 是List的子类接口,可以用List list = new ArrayList();
只有List可以利用索引循环取元素,也可以用迭代器取。只有List!
3,快速失败迭代器,集合和迭代器同时对元素进行修改,就会发生异常,因为获取集合的迭代器后,对集合内元素进行修改,迭代器是不知道的,这样迭代就会出问题。所以不要在迭代过程中使用集合操作元素,容易出现异常。
但只有List容器有一个listIterator()方式可以解决这个问题,因为它提供了多种修改元素的方法。
4,常用的List有:Vector,ArrayList,LinkList
5,Vector:内部是数组数据结构,具备List所有功能特点。1.0就出现,最早,是同步线程安全的。但是增删都很慢,Vector几乎不用,枚举遍历也不用了。
----ArrayList:内部是数组数据结构,不同步线程不安全。替代了Vector,查询快因为空间局部性(缓存的存在),数组迭代效率更高 直接寻址,自己加锁。
----LinkedList:内部是链表数据结构,是不同步的,增删速度快。jdk1.6以后的新方法更好用:用offerFirst代替addFirst,用peekFirst peekLast代替getFirst getLast,用pollFirst代替removeFirst方法。
6,扩大数组方法是创建一个新数组,把原来数组里的数据复制到新数组里。
7,默认存储的是Object类型,需要向下转型才能使用实际存入的类方法。
8,eclipse快捷键
1, Set集合:不包含重复元素,无序,方法和Collection方法一致
|-- HashSet:内部数据结构是哈希表,是不同步的,hashSet是通过哈希值hashCode()映射,来确定存放位置,哈希值和对象地址是不同的。而hashCode()通过值计算,所以基本数据类型的值如果一样,hashCode()值也基本一样,但自定义的类不一定,要重写。
那么,哈希表如何确定元素是否相同:1. 判断哈希值对应位置有无元素(哈希值不同,内容肯定不相同,因为hashCode()是通过值映射,比如"ab" "ba"哈希值相同内容不同)。如果哈希值相同,再判断内容是否相同用equals()。2. 判断哈希值相同,其实是判断的是对象的hashCode方法(有必要的话需要重写);判断内容相同,用的是equals方法(有必要的话需要重写)。 至于我的理解,就是按当前对象放入容器里,如果碰到该映射位置上有对象,就判断一下,哈希值不相同或者哈希值同内容不同,就存下一个空间,一直延顺到空位置上,或者有同哈希值同内容的元素而退出来,这里操作会调用这两个函数。
注意:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。 remove() contains()方法也是用equals来找元素内容一样的元素而不是找同地址的。所以一定要复写好equals方法。
|-- LinkedHashSet:按插入有序,不可重复。
|-- TreeSet:原理二叉排序树,按字典(自然顺序)有序,是不同步的,自然顺序由compareTo方法决定。compareTo判断元素唯一的方法:就是根据compareTo()方法返回结果是否是0,是0就是相同元素,不存(这里要小心,等于0的话会去掉),所以要使用TreeSet容器,就需要实现Comparable接口,覆盖compareTo方法。
如果不要按照对象中具备的自然顺序进行排序,因为有时使用的类不是我们自定义的,使用TreeSet集合第二种排序方式二: 让集合自身具备比较功能
TreeSet(Comparator super [E]> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。经常需要写比较器!!* 也就是一个实现Comparable接口的类,并覆盖compare方法,传给构造函数,来定义自己的排序规则。
1,Map:一次添加一对元素。Collection 一次添加一个元素。
---Map页称为双列集合,Collection集合称为单列集合。
---其实Map集合中存储的就是键值对,而且必须保证键的唯一性。
常用方法:添加put,删除clear remove,判断containskey containsValue,获取get size,迭代方法keySet Values entrySet()找Map.Entry这个静态的映射项类对象。
2,Map常用的子类:
|-- Hashtable: 内部结构上哈希表,是同步,不允许null作为键和值。
------Properties: 用来存储键值对应的配置文件信息,可以和IO技术相结合。
|-- HashMap: 内部结构上哈希表,不说同步的,允许null作为键和值,重写hashCode, equals方法。
|--TreeMap: 内部结构上二叉树,可以对Map集合中的键进行排序,能排序的是Tree*。泛型。jdk1.5出现的安全机制。
好处是:
1,将运行时期的问题ClassCastException转到了编译时期,确保编译时类型的安全。运行时会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
那么为什么擦除?为了兼容以前不认识泛型的类加载器。后面运行用泛型的补偿来自动向下转型,原理是用getClass方法,再getName得到类名。
2,避免了强制转换的麻烦。
那么<>什么时候用?当操作的饮用数据类型不确定的时候,就使用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。
3,可以自定义泛型类,用来接收类中要操作的引用数据类型。public Class Tool{}; 当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。
4,泛型方法。publicvoid show(W str);(泛型必须写在返回类型符前面)。 当方法为静态时,不能访问泛型类中的泛型,如果要访问,只能把泛型定义在方法上,也就是做静态泛型方法。
5,泛型接口。实现泛型接口要在子类中确定泛型类型,如果还是不确定类型,就定义好泛型类来实现接口,使用泛型来传给接口的泛型。比如接口是,实现它的类是 ,看你参数类型明确不明确。
6,泛型的通配符:? 传参的时候不明确类型,使用(Collection> arr)来传递容器泛型不明确的参数。直接(Collection arr)也是一样 这是老版本。使用?通配符的情况是仅在不明确类型而且不对这种类型的对象进行操作的时候,用?通配符,这种情况比较多。
注意:不能用ArrayList的变量传给Collection 的变量,只认Person。这时候可以用通配符 extends Person>来帮忙。
7,泛型的限定。上限: extends Person>,只接受P和P的子类,一般存元素的时候用上限,因为这样取出都是按上限类型进行运算,安全。
--------下限: super P>,只接受P和P的父类。一般对元素进行取出操作时,可以用下限。综上,集合选择的技巧:
需要唯一吗?
需要:Set
---需要定制顺序:
------需要:TreeSet
------不需要:HashSet
------但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
---需要频繁增删吗?
------需要:LinkedList
------不需要:ArrayList
具体集合中源码及其原理解析看这个博客集合框架的工具类:有 Collections Arrays 。
Collections:
1,Collections.reverseOrder();想逆序,用这个。
2,将非同步类改为同步,见视频19-16(就加了个同步代码块再封装一下),或者直接用Collections中定义好的工具方法。
Arrays:
1,Array.toString(); 将数组输出成字符串形式。
2,Arrays.asList(); 将数组转成列表来使用。要注意:因为数组的长度的固定,所以对于这种集合的增删方法是不可以使用的,否则会发生UnsupportedOperationException。
并且,如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行存储。
如果数组中的元素是基本类型,那么会将数组作为集合元素存储。
3,集合转成数组:用Collection接口中的.toArray(); 需要传入一个指定类型的数组大小写上,太长会给null,太短会固化到集合长度。
可以对集合中的元素操作方法进行限定,就是不允许增删。for each语句。
1,格式:
for(类型 变量:Collection集合|数组){}
2,foreach循环的局限是,不能对集合或数组修改,如重新赋值,因为它是先取出元素给单独的一个变量来用的,比如P p = ps[i]; 所以foreach仅仅在不对集合修改的情况下使用非常方便,比如遍历打印。而且这个for不能执行指定次数的循环。
3,另一个局限是:
只能遍历单列集合,也就是不能遍历Map集合。解决办法是:用keySet()方法转单列就可以用了。函数可变参数:public static int add(int... arr){}
可以直接写多个参数在里面或者数组,而不用必须先创建一个数组再传进入。如果要传另外的单个参数,要放可变参数前面,也即可变参数必须放列表结尾。应用有asList()方法。静态导入。import static java.util.Collections.sort; 其实是导入类中的静态成员。如果名字冲突,那么一定要加包名。静态导入用的不多。
**System类中的方法和属性都是静态的。常见方法:
1,long currentTimeMillis(); 获取当前时间的毫秒值
2,System.getProperties();获取系统的属性信息,并存储到Properties(Map子类)集合中。 Properties集合中存储都是String类型的键和值。最好使用它自己的存储和取出方法来完成对元素的操作。
重点:虚拟机启动的时候都会先拿该系统上的信息,因为不同系统对应着不同的习惯,比如换行符在win上是/r/n,在unix上是/n,为了保证Java的强大移植性,用系统拿到的信息来写这些易变化的符号。比如换行符用System.getProperty("line.separator");或System.lineSeparator(); 就是换行符。可以把它最终化为静态常量。Line.separator这些键信息可以在getProperties方法里查到。
3,System.setProperty();往系统里插入一些信息。F2显示代码信息。可以在editor里关掉自动悬停。Runtime运行时对象。没有构造函数意味着这个类不能创建对象,因为构造函数被私有化了。如果一个类没有构造函数,这类里的方法都是静态的,在这个类里不一样,但是百分之百有一个静态方法,返回这个对象,这是单例设计模式。常见方法有:
1,exec("notepad.exe xxxx路径"); 用记事本打开对应文件。返回值是Process对象,这个对象只有进程才创建,我们可以获取。
2,Process,有一个destroy()可以杀死由java打开的进程。Math类:提供了操作数学运算的方法。都是静态的。有:
1,ceil();floor();round();
2,max();min();pow();
3,random(); util里有Random类,更丰富的方法。比如new Random().nextInt(6) + 1;Date日期类默认初始化为当前时间,也可指定毫秒值来做。所以:
1,毫秒值 -> 日期对象:通过new Date(timeMillis); Date打印出来就是日期。
2,日期对象 -> 毫秒值:通过getTime方法。还可以通过setTime()设置
3,调整日期输出格式:使用DateFormat这个抽象类,通过其静态工厂方法获取实例对象,并使用该实例的format()格式化方法转换日期对象成合适的字符串,还可以指定风格。这里的静态工厂方法放回的是SimpleDateFormat这个子类。
4,想要自定义格式,可以用SimpleDateFormat("yyyy--MM--dd");或者在DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);来指定风格。
5,将日期格式的字符串 ---> 日期对象,使用的是DateFormat类中的parse()方法。Calendar类 基本取代了Date。也是个抽象类,需要get实例,它弄成了很多字段。
可以通过键取值,键用类的静态字段来写 c.get(Calendar.YEAR)。小心月从0开始,周日按序到周六。
set(2011, 3, 20);设置时间,记得月数要加1。
add(Calendar.YEAR, -2); 在该日历对象上加-2年,操作月也类似。IO流: 是相对于内存而言的。内存到硬盘是输出(写),硬盘到内存是输入(读)。分为字节流和字符流,处理字节的数据流对象是字节流。后来出现了码表,如ascii表,java内置的unicode表-->无论什么字符都用两个字节表示。
字符流的由来:其实就是 字节流读取文字字节数据后,不直接操作而现查指定的编码表,获取对应的文字,再对这个文字进行操作。也就是:字节流 + 编码表。
字节流的两个顶层父类:
1,InputStream 2,OutputStream
字符流的两个顶层父类:
1,Reader 2,Writer
这些体系的子类都以父类名做为后缀。
一:往文件中写入字符数据用FileWriter类的write()方法,先会写到临时存储缓冲区中,要用flush()方法刷新下就写入。这会使用系统的写入资源,记得close();在关闭前会先调用flush刷新缓冲中的数据要目的地。
如果想要续写同一个文件,就用带第二个参数的构造函数FileWriter(xxx, true);
二:IO异常处理,打开会出异常,写入会出异常比如写失败,关闭出异常,挂了wins返回异常。
记住,流对象的初始化,在try外面创建引用变量,在try里面进行对象初始化,因为流对象创建都有异常发生。常见做法,打开和读写一起try,关闭在finally里再try。
三:读取方法:fr.read()读出一个字符,读出一个字符数组。
小技巧,读出来的字符数组可以用new String(buffer, 0, num)将指定范围的数组字符内容转换成字符串形式,可以用来写入另一个文件或输出。或者直接用fw.write(cbuff, off, len);来指定要写入的数组内容范围。
四:缓冲区对象:提高效率。创建缓冲区必须要有被缓冲的对象。
写缓冲区:FileWriter,用BufferedWriter.write()和.flush()写入文件中,记得关闭缓冲区.close();其实关闭的就是被缓冲的流对象。这对象又给了个newLine()写换行符的方法。
读缓冲区:FIleReader,多了readLine()读一行的方法。原理是在缓冲区里读到换行符就停止,把临时StringBuilder内容输出。
装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决 比如BufferedReader等。把旧类传参给新类。与继承一样可以进行功能的扩展增强。
有什么区别? 用不断继承来扩展会产生很多关系,变得臃肿,用装饰更灵活。
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
五:LineNumberReader,可以读取行号的带缓冲技术读文件。字符流常用的就是FileReader BufferedReader 都是Reader的子类。
六:字节流:缓冲区是字节数组,父类是InputStream OutputStream,操作文件就用FileInputStream。写数据是直接写入目的地,可以不用flush(),但是close()要做 关闭资源。.available()可获取文件大小,用这个可以定义一个刚刚够的缓冲区大小 但易溢出,或者分段读。BufferedOutputStream有Flush();的用法,但是不要每写一个字节就刷一次会很慢。
注意:千万不要用直接与文件读一个写一个 不用缓冲区。
字符流读完字节查表转字符,如果表里查不到内容 会用未知字符区的数据来表示 元数据和目标数据不会一致,所以别用字符流读非文本文件。-
键盘录入。
1,读取键盘输入 System.in 是InputStream对象,相当于打开一个字节流文件(当个文件使用就行)。查看这个类的方法比如read();clode();关一次就没了别关。cmd + c是-1。
2,InputStreamReader 是字节流通向字符流的桥梁(解码 硬盘字节文件到内存字符),将字节流转换成字符流。想要从键盘读一行,要用缓冲流的readLine()。
2,OutputStreamWriter 是字符流通向字节流的桥梁(编码 内存字符到硬盘字节文件),将字节流转换成字符流来使用。用这个来输出记得flush();
3,总结起来四个点:
获取控制台输入流:InputStream in = System.in;
字节流变字符流:InputStreamReader isr= new InputStreamReader(in);
对字符流高效装饰:BufferedReader bufr = new BufferedReader(isr);
String line = bufr.readLine();
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
bufw.write(line.toUpperCase());
记得整合成一条语句。
4,流的操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
(1) 明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer(2) 明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream(3) 明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流(4) 是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
========================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
========================================================
将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
问:那么什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
注意: FileWriter fw = new FileWriter("文件名");
封装的就是
OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("文件名"));
它是为了简化书写。所以FileWriter 会是OutputStreamWrite的子类。而前者其实就是转换流默认使用了本机默认码表,后者可以支持指定特定的码表。
简单来说:字节流 + 本机默认编码表(Mac上是UTF-8) 就是是 !!转换流 == 字符流!!。这是按照默认码表操作文件的便捷类。UTF-8有3个字节表示中文。
- FIle类封装文件或文件夹。 和流不同,流对象只能操作文件上的数据。和File类封装文件和文件夹成对象,对文件和文件夹的属性信息进行操作。File还可以封装一个父路径对象。其子段可以直接获取本系统的文件 路径 行分隔符,File.separator == System.getProperties(line.separator);等。
1,相对路径名:就是默认使用当前目录,getParent()获得的是空,但是可以用getAbsoluteFile()来获得绝对路径名或绝对路径形式的文件。绝对路径名:具体路径,getParent()不为空。
2,可以根据lastModified()是否改变,来读取一次文件,以获得最新的文件信息。
3,创建和删除文件:
(1) 创建文件:File f = File("file.txt"); f.createNewFile();
(2) 创建多级目录:mkdirs();
(3) 删除文件:f.delete();f.deleteOnExit();指定好退出时删除文件。对于有内容的文件夹不能删。
4,判断:
(1) 是否存在:f.exists();
(2) 是不是文件 目录:f.isFile(); f.isDirectory(); 操作作判断用。
5, 重命名:f.renameTo(); 抽象路径名也会改变,改了路径名会导致剪切操作。
6,列出文件系统根目录:File.listRoots();
7,获得指定分区容量情况:getFreeSpace(); getTotalSpace(); getUsableSpace();
8,列出指定路径下的文件和文件夹:list(); 注意File必须是目录,否则NullPointerException无法创建返回值。
9,带过滤器的列出文件:
接口 FilenameFilter accept()实现这方法。list(new FilterbyName()); 实现时可以创建一个成员变量,来指定要满足的条件的信息。
10,列出次抽象路径名表示的目录中的文件,不是文件名; listFiles();
11,删除一个带内容的目录。必须用(递归)深度遍历文件夹 必须从最里面往外删, - Properties类。Map里有个HashTable可以和IO流相结合,HashTable 的子类Properties 是一个没有泛型的类,其结构是:
Map
|---HashTable
|---|---Properties:
.
Properties特点:
1,该集合中的键和值都是字符串类型。
2,集合中的数据可以保存到流中,或者从流获取。
.
通常该集合用于操作以键值对形式存在的配置文件 xml sql。
.
Properties集合的操作:
1,存 setProperty("xx", "xx");
2,取 getProperty("xx");
3,取出所有 stringPropertyNmaes();
4,改 setProperty("xx", "xx");
5,与IO结合 list() 可以将列表信息输出到对应的流。比如打印到控制台上。 System.getProperties()返回的就是这个类,可以直接list到输出流。
6,数据持久化 存储到硬盘上 store(x, "xx");
7,读取文件上的属性信息,要求:
--集合中的数据来自一个文件,保证数据是键值对,使用读取流。
load() 可以将文件中的键值对信息加载到Prop对象上。
读行并按=切就可以模拟load()方法。
8,操作读取或写入文件时,可以先封装成File来看这个抽象路径名是否存在,不存在可以新建,这样就不会在读取的时候报错。
9,常用来修改一个配置文件信息,先用读取字符流读进prop,再用输出字符流重写整个配置文件。
10,map + io = Properties
11,简单的配置文件用Properties键值对,复杂的用.xml文件。 - 打印流PrintWriter PrintStream --可以直接操作输入流和文件。PrintSteam 唯一一个不抛IO异常的IO对象,字符流用PrintWriter。
---提供打印方法可以对多种数据类型值进行打印。并保持数据的表示形式。
---他不会抛IOException
其构造函数,接受三种数据类型:
---字符串路径
---File文件
---字节输出流
PrintWriter功能和PrintStream功能基本一样,就多了一个接受Writer类的构造函数。 - 序列流SequenceInputStream --对多个流进行合并。
---对于字节输入流,可以用数组来直接读取,read(b);返回值是读入的数据长度,无数据可读返-1。