chapter 6 继承与多态
6.1何谓继承
1.继承的定义:继承就是避免多个类间重复定义共同行为。
2.总结:教材中通过设计一款RPG游戏的部分代码向我们展示了“重复”程序代码的弊端,为了改进,我们引进了继承的概念。在我们自我学习的过程中,通过一次次的改进,也曾经得到这样的启示:在不同的信息类中,有多少的属性就设置多少个变量,给每个属性分别给予设置和获取的方法,以来可以将属性设为private时仍然可以存取引用数据,二来当多个类拥有同样属性和同样的设置、获取方法时,我们可以将其利用继承于父类的方式精简代码,提高可读性和简洁性,并未之后对代码的修改提供方便。
简而言之,就是需要我们通过理解和提炼,将相同的程序代码提升为父类。
3.继承的格式:我们通过关键字“extends”来连接子类和父类,这表示子类会扩充、继承父类的行为。
4.引入类图:类图是用于建模帮助我们理解类中内容、父类子类间关系的工具,在教材161页有提示,另外在毕向东老师Java讲解视频中也有提及,并且更为详细。
5.is-a:继承中,子类与父类间会有is-a的短息,即“是一种”的关系。简单说就是:子类是父类的一种。教材中通过剑士(子类)是一种角色(父类)的例子来说明。
此处引入了编译与运行的通过判断的讲解,在这当中提及了一个操作:“如何让编译程序闭嘴”。这其实是一种投机的操作,编译虽然能够无错误地通过,但运行结果要自负。
6.重载:在并行的类中定义同名方法,这就是重载方法的运用。
7.多态:引入多态,其实是对重载的改进。当并行类很多并且他们拥有共同的特点时,我们可以将其运用继承修改代码,并在此基础上引入多态。多态其实就是使用单一接口操作多种类型的对象。这种写法的好处就是能够具有更高的可维护性。
8.重新定义:在继承父类之后,定义于父类中相同的方法不熟,但执行内容不同,这就是重新定义(Override)。
技巧:在某个子类中某个方法前标注@Override,表示要求编译程序检查,该方法是不是真的重新定义了父类中国某个方法,若不是,则编译错误。
9.抽象方法:如果某方法区块中没有任何程序代码操作,可以使用abstract表示该方法为抽象方法,该方法不用撰写{}区块,至二级用“;”结束即可。
10.抽象类:若一个类中有方法没有操作并且表示为abstract,则这个类就不能用来生成实例,即是抽象类,要在class前面表示abstract。
11.抽象类的继承:若子类继承抽象类,对于抽象方法有两种做法:一是继续标示该方法为abstract,该子类因此也是一个抽象类,必须在class前面表示abstract;另一种做法就是操作抽象方法。若两种做法都没有实施,就会引发编译错误。
6.2继承语法细节
1.protected:
(1)private定义的属性不能直接在子类中存取,只能通过获取方法来取得。若只想让子类可以直接存取属性变量又不想完全开放访问权限时,可将他们定义为protected。
(2)被声明为protected的成员,相同包中的类可以直接存取,不同包中的类需要在继承后的子类中存取。
(3)在写程序时,若一开始对权限未知,一般将成员设定为private。
2.重新定义的细节
(1)super:若子类想取得父类中的方法定义,可在调用方法前,加上super关键字。(例见Game6 Role.java;Game6 SwordsMan.java;Game6 Magician.java)
(2)注意:可以使用super关键字调用的父类方法,不能定义为private。并且对于父类中的方法权限,只能扩大但不能缩小。
(3)JDK5的改进:在JDK5之前,重新定义方法时除了可以定义权限较大的关键字外,其他部分必须与父类中方法签署完全一致,例如返回值类型。在JDK5及之后,只要返回类型是父类中方法返回类型的子类,也是可以通过编译的。
(4)提示:static方法属于类拥有,如果子类中定义了相同签署的static成员,该成员属于子类所有,而非重新定义,static方法也没有多态,因为对象不会个别拥有static成员。
3.构造函数:
(1)继承构造函数定义的先后顺序:创建子类石磊后,会先进行父类定义的初始流程再进行子类中定义的初始流程。即在创建子类实例后,会先执行父类构造函数定义的流程,再执行子类构造函数定义的流程。
(2)构造函数的重载:父类中可重载多个构造函数,如果子类构造函数中没有指定执行父类中哪个构造函数,会默认调用父类中无参数的构造函数。
(3)this()和super()只能择一调用,而且一定要在构造函数第一行执行。
4.final关键字:
(1)final:如果对象数据成员被声明为final,但没有明确使用=指定值,表示延迟对象成员值的指定,在构造函数执行流程中,一定要有对该数据成员指定值得操作,否则编译错误。
(2)继承中的final:若class前使用了final,则这个类是末类,不能被继承。若方法被限定为final,子类也不能重新定义final方法了。
5.java.lang.Object:如果定义类时没有使用extends关键字定义继承任何类,则代表它继承java.lang.Object。即它是所有类的最上层父类。
6.垃圾收集:对于不再有用的对象,JVM有垃圾收集机制,收集到的垃圾对象所占据的内存空间会被垃圾收集器释放。在执行流程中,无法通过变量参考的对象,就是GC认定的垃圾对象。
7.抽象类:重点通过例子体现继承思想。(代码包中有对应代码)
附加:总结补充
通过我对Java视频的复习,我将继承与多态相关知识点记录如下:
1.继承的优点:
(1)提高了代码的复用性。 (2)让类与类之间产生了关系,提供了另一个特征多态的前提。
2.父类的由来:其实是由多个类不断向上抽取共性内容而来的。java中对于继承,java只支持单继承。java虽然不直接支持多继承,但是保留了这种多继承机制,进行改良。
单继承:一个类只能有一个父类。 多继承:一个类可以有多个父类。
3.不支持多继承的原因:当一个类同时继承两个父类时,两个父类中有相同的功能,那么子类对象调用该功能时,运行哪一个呢?因为父类中的方法中存在方法体。但是java支持多重继承。A继承B,B继承C,C继承D。多重继承的出现,就有了继承体系。体系中的顶层父类是通过不断向上抽取而来的。它里面定义的该体系最基本最共性内容的功能。所以,一个体系要想被使用,直接查阅该系统中的父类的功能即可知道该体系的基本用法。那么想要使用一个体系时,需要建立对象。建议建立最子类对象,因为最子类不仅可以使用父类中的功能。还可以使用子类特有的一些功能。
简单说:对于一个继承体系的使用,查阅顶层父类中的内容,创建最底层子类的对象。
4.子父类出现后,类中的成员的特点:
(1)成员变量:子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。如果想要调用父类中的属性值,需要使用一个关键字:super 。
This:代表是本类类型的对象引用。 Super:代表是子类所属的父类中的内存空间引用。
注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
(2)成员函数:当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是函数的另一个特性:覆盖(复写,重写)。当一个类的功能内容需要修改时,可以通过覆盖来实现。
(3)构造函数:发现子类构造函数运行时,先运行了父类的构造函数。原因是子类的所有构造函数中的第一行,其实都有一条隐身的语句super();
注意:子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super(); 如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。
5.继承的细节:
在方法覆盖时,注意两点:
子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。 覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖)
6.继承的弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者复写的。此时则需要引进final来解决。
7.final特点:
这个关键字是一个修饰符,可以修饰类,方法,变量。 被final修饰的类是一个最终类,不可以被继承。 被final修饰的方法是一个最终方法,不可以被覆盖。 被final修饰的变量是一个常量,只能赋值一次。
其实这样的原因的就是给一些固定的数据起个阅读性较强的名称。
不加final修饰不是也可以使用吗?那么这个值是一个变量,是可以更改的。加了final,程序更为严谨。常量名称定义时,有规范,所有字母都大写,如果由多个单词组成,中间用 _ 连接。
8.抽象类 abstract:
抽象:不具体,看不明白。抽象类表象体现。
在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。
抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
9.抽象类的特点:
抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。 抽象方法只定义方法声明,并不定义方法实现。 抽象类不可以被创建对象(实例化)。 只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
10.抽象类的细节:
(1)抽象类中是否有构造函数?有,用于给子类对象进行初始化。 (2)抽象类中是否可以定义非抽象方法? 可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。 (3)抽象关键字abstract和哪些不可以共存?final , private , static (4)抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
chapter7 接口与多态
1.定义:关键字interface。接口中包含的成员,最常见的有全局常量、抽象方法。
注意:接口中的成员都有固定的修饰符。 成员变量:public static final 成员方法:public abstract
interface Inter{ public static final int x = 3; public abstract void show(); }
3:接口中有抽象方法,说明接口不可以实例化。接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
4:类与类之间存在着继承关系,类与接口中间存在的是实现关系。继承用extends ;实现用implements ;
5:接口和类不一样的地方,就是,接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多现实来体现。
6:一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。
7:其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口。
接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)
1:接口是对外提供的规则。 2:接口是功能的扩展。 3:接口的出现降低了耦合性。
抽象类与接口:
抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。
抽象类和接口的共性:都是不断向上抽取的结果。
抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。 接口需要被实现,而且可以多实现。 2:抽象类中可以定义非抽象方法,子类可以直接继承使用。 接口中都有抽象方法,需要子类去实现。 3:抽象类使用的是 is a 关系。 接口使用的 like a 关系。 4:抽象类的成员修饰符可以自定义。 接口中的成员修饰符是固定的。全都是public的。
在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。
chapter8 异常
1.异常:就是不正常。程序在运行时出现的不正常情况。其实就是程序中出现的问题。这个问题按照面向对象思想进行描述,并封装成了对象。因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。
2.出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系。
--------java.lang.Throwable:
Throwable:可抛出的。
|--Error:错误,一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。
|--Exception:异常,可以有针对性的处理方式
无论是错误还是异常,它们都有具体的子类体现每一个问题,它们的子类都有一个共性,就是都以父类名才作为子类的后缀名。
这个体系中的所有类和对象都具备一个独有的特点;就是可抛性。
可抛性的体现:就是这个体系中的类和对象都可以被throws和throw两个关键字所操作。
class ExceptionDemo{ public static void main(String[] args) { // byte[] buf = new byte[1024*1024*700];//java.lang.OutOfMemoryError内存溢出错误 } }
在开发时,如果定义功能时,发现该功能会出现一些问题,应该将问题在定义功能时标示出来,这样调用者就可以在使用这个功能的时候,预先给出处理方式。
3.标示方法:通过throws关键字完成,格式:throws 异常类名,异常类名...标示后,调用者在使用该功能时,就必须要处理,否则编译失败。
4.处理方式:1、捕捉;2、抛出。
对于捕捉:java有针对性的语句块进行处理。
try { 需要被检测的代码; } catch(异常类 变量名){ 异常处理代码; } fianlly{ 一定会执行的代码; }
--------------------------------------------------------
catch (Exception e) { //e用于接收try检测到的异常对象。 System.out.println("message:"+e.getMessage());//获取的是异常的信息。 System.out.println("toString:"+e.toString());//获取的是异常的名字+异常的信息。 e.printStackTrace();//打印异常在堆栈中信息;异常名称+异常信息+异常的位置。 }
异常处理原则:功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样的处理有针对性,抛几个就处理几个。
特殊情况:try对应多个catch时,如果有父类的catch语句块,一定要放在下面。
throw 和throws关键字的区别:
throw用于抛出异常对象,后面跟的是异常对象;throw用在函数内。 throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在函数上。
通常情况:函数内容如果有throw,抛出异常对象,并没有进行处理,那么函数上一定要声明,否则编译失败。但是也有特殊情况。
异常分两种:
1:编译时被检查的异常,只要是Exception及其子类都是编译时被检测的异常。 2:运行时异常,其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。
编译时被检查的异常和运行时异常的区别:
编译被检查的异常在函数内被抛出,函数必须要声明,否编译失败。 声明的原因:是需要调用者对该异常进行处理。 运行时异常如果在函数内被抛出,在函数上不需要声明。 不声明的原因:不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以,不让调用处理的,直接让程序停止,由调用者对代码进行修正。
定义异常处理时,功能内部如果出现异常,如果内部可以处理,就用try;如果功能内部处理不了,就必须声明出来,让调用者处理。
自定义异常:当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。
对于除法运算,0作为除数是不可以的。java中对这种问题用ArithmeticException类进行描述。对于这个功能,在我们项目中,除数除了不可以为0外,还不可以为负数。可是负数的部分java并没有针对描述。所以我们就需要自定义这个异常。
自定义异常的步骤:
1:定义一个子类继承Exception或RuntimeException,让该类具备可抛性。
2:通过throw 或者throws进行操作。
异常的转换思想:当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。
当异常出现后,在子父类进行覆盖时,有了一些新的特点:
1:当子类覆盖父类的方法时,如果父类的方法抛出了异常,那么子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类,不能抛出其他异常。 2:如果父类抛出了多个异常,那么子类在覆盖时只能抛出父类的异常的子集。
注意:
如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws。 如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。
常见异常:
1、脚标越界异常(IndexOutOfBoundsException)包括数组、字符串;
空指针异常(NullPointerException)
2、类型转换异常:ClassCastException
3、没有这个元素异常:NullPointerException
4、不支持操作异常;
异常要尽量避免,如果避免不了,需要预先给出处理方式。比如家庭备药,比如灭火器。
chapter9 collection和maps
1.Map集合:
|--Hashtable:底层是哈希表数据结构,是线程同步的。不可以存储null键,null值。
|--HashMap:底层是哈希表数据结构,是线程不同步的。可以存储null键,null值。替代了Hashtable.
|--TreeMap:底层是二叉树结构,可以对map集合中的键进行指定顺序的排序。
Map集合存储和Collection有着很大不同:
Collection一次存一个元素;Map一次存一对元素。
Collection是单列集合;Map是双列集合。
Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。
特点:要保证map集合中键的唯一性。
1,添加。
put(key,value):当存储的键相同时,新的值会替换老的值,并将老值返回。如果键没有重复,返回null。 void putAll(Map);
2,删除。
void clear():清空 value remove(key) :删除指定键。
3,判断。
boolean isEmpty(): boolean containsKey(key):是否包含key boolean containsValue(value) :是否包含value
4,取出。
int size():返回长度 value get(key) :通过指定键获取对应的值。如果返回null,可以判断该键不存在。当然有特殊情况,就是在hashmap集合中,是可以存储null键null值的。 Collection values():获取map集合中的所有的值。
5,想要获取map中的所有元素:
原理:map中是没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。
★ 把map集合转成set的方法:
Set keySet(); Set entrySet();//取的是键和值的映射关系。
Entry就是Map接口中的内部接口;
为什么要定义在map内部呢?entry是访问键值关系的入口,是map的入口,访问的是map中的键值对。
1.取出map集合中所有元素的方式一:keySet()方法。
可以将map集合中的键都取出存放到set集合中。对set集合进行迭代。迭代完成,再通过get方法对获取到的键进行值的获取。
Set keySet = map.keySet(); Iterator it = keySet.iterator(); while(it.hasNext()) { Object key = it.next(); Object value = map.get(key); System.out.println(key+":"+value); }
--------------------------------------------------------
2.取出map集合中所有元素的方式二:entrySet()方法。
Set entrySet = map.entrySet(); Iterator it = entrySet.iterator(); while(it.hasNext()) { Map.Entry me = (Map.Entry)it.next(); System.out.println(me.getKey()+"::::"+me.getValue()); }
--------------------------------------------------------
使用集合的技巧:
看到Array就是数组结构,有角标,查询速度很快。 看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast(); 看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。 看到tree就是二叉树,就要想到排序,就想要用到比较。
比较的两种方式:
一个是Comparable:覆盖compareTo方法;
一个是Comparator:覆盖compare方法。
LinkedHashSet,LinkedHashMap:这两个集合可以保证哈希表有存入顺序和取出顺序一致,保证哈希表有序。
集合:当存储的是一个元素时,就用Collection。当存储对象之间存在着映射关系时,就使用Map集合。保证唯一,就用Set。不保证唯一,就用List。
------------------------------------------------------------------------------------------------
Collections:它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。
静态方法:
Collections.sort(list);//list集合进行元素的自然顺序排序。 Collections.sort(list,new ComparatorByLen());//按指定的比较器方法排序。 class ComparatorByLen implements Comparator<String>{ public int compare(String s1,String s2){ int temp = s1.length()-s2.length(); return temp==0?s1.compareTo(s2):temp; } } Collections.max(list); //返回list中字典顺序最大的元素。 int index = Collections.binarySearch(list,"zz");//二分查找,返回角标。 Collections.reverseOrder();//逆向反转排序。 Collections.shuffle(list);//随机对list中的元素进行位置的置换。
将非同步集合转成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定义一个类,将集合所有的方法加同一把锁后返回。
Collection 和 Collections的区别:
Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。 Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。
chapter10 输入输出
IO流:用于处理设备上数据。
流:可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,对象都存在IO包中。
流也进行分类:
1:输入流(读)和输出流(写)。
2:因为处理的数据不同,分为字节流和字符流。
①字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
②字符流:因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
注意:流的操作只有两种:读和写。
流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。
字节流:InputStream OutputStream
字符流:Reader Writer
在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。
public static void main(String[] args) throws IOException { //读、写都会发生IO异常 /* 1:创建一个字符输出流对象,用于操作文件。该对象一建立,就必须明确数据存储位置,是一个文件。 2:对象产生后,会在堆内存中有一个实体,同时也调用了系统底层资源,在指定的位置创建了一个存储数据的文件。 3:如果指定位置,出现了同名文件,文件会被覆盖。 */ FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException /* 调用Writer类中的write方法写入字符串。字符串并未直接写入到目的地中,而是写入到了流中,(其实是写入到内存缓冲区中)。怎么把数据弄到文件中? */ fw.write("abcde"); fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。 fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。 }
close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。
--------------------------------------------------------------------------------------------------------------------
io异常的处理方式:io一定要写finally;
FileWriter写入数据的细节:
1:window中的换行符: 两个符号组成。 linux: 。
2:续写数据,只要在构造函数中传入新的参数true。
3:目录分割符:window \ /
public static void main(String[] args) { FileWriter fw = null; try { fw = new FileWriter("demo.txt",true); fw.write("abcde"); } catch (IOException e ){ System.out.println(e.toString()+"...."); } finally{ if(fw!=null) try{ fw.close(); } catch (IOException e){
System.out.println("close:"+e.toString()); } } }
FileReader:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
import java.io.*; class FileReaderDemo { public static void main(String[] args) throws IOException { /* 创建可以读取文本文件的流对象,FileReader让创建好的流对象和指定的文件相关联。 */ FileReader fr = new FileReader("demo.txt"); int ch = 0; while((ch = fr.read())!= -1) { //条件是没有读到结尾 System.out.println((char)ch); //调用读取流的read方法,读取一个字符。 } fr.close(); } }
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
import java.io.*; class FileReaderDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。 //因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。 char[] buf = new char[1024]; int len = 0; while(( len=fr.read(buf)) != -1) { System.out.println(new String(buf,0,len)); } fr.close(); } }
IO中的使用到了一个设计模式:装饰设计模式。
装饰设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对被包装对象进行包装;
包装类和被包装对象要实现同样的接口; 包装类要持有一个被包装对象; 包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;
字符流:
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。
|---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
|---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
|---CharArrayReader:
|---StringReader:
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
|---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
|---PrintWriter:
|---CharArrayWriter:
|---StringWriter:
字节流:
InputStream:是表示字节输入流的所有类的超类。
|--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
|--- FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--- BufferedInputStream:该类实现缓冲的输入流。
|--- Stream:
|--- ObjectInputStream:
|--- PipedInputStream:
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|--- FileOutputStream:文件输出流是用于将数据写入 File
或 FileDescriptor
的输出流。
|--- FilterOutputStream:此类是过滤输出流的所有类的超类。
|--- BufferedOutputStream:该类实现缓冲的输出流。
|--- PrintStream:
|--- DataOutputStream:
|--- ObjectOutputStream:
|--- PipedOutputStream:
缓冲区是提高效率用的:
BufferedWriter:是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
FileWriter fw = new FileWriter("bufdemo.txt"); BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。 for(int x=0; x<4; x++){ bufw.write(x+"abc"); bufw.newLine(); //写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。 bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。 } bufw.close();//关闭缓冲区,其实就是在关闭具体的流。
BufferedReader:
FileReader fr = new FileReader("bufdemo.txt"); BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null){ //readLine方法返回的时候是不带换行符的。 System.out.println(line); } bufr.close();
流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
流的操作规律:
1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader; 数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
如果是:数据源:Reader
数据汇:Writer
如果不是:数据源:InputStream
数据汇:OutputStream
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。如果需要就进行装饰。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。
发现转换流有一个子类就是操作文件的字符流对象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
FileReader fr = new FileReader("a.txt"); InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上两句代码功能一致,
如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。
如果需要制定码表,必须用转换流。
转换流 = 字节流+编码表。
转换流的子类File = 字节流 + 默认编码表。
凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
chapter 12 Lambda
这部分内容书上讲的很详细很多代码可以用来参照,知识点就不列举了。另外由于这个是JDK 8的新特性,我在网上找了其他一些探究讲解,可以用来参考。
链接如下:
http://developer.51cto.com/art/201404/435591.htm
chapter 14 NIO和NIO2
这部分内容我至今看的不是很懂,只是看了书上内容反复看了代码,但是实际上并不能理解多少。网上也能看到一些资料,但是对自己启发也不是很大= =。还得继续研读。
总结
这六个章节是我负责来敲代码和做记录的部分。由于自身的基础和对一些模块的学习欠缺导致我看起来也很吃力。在寒假扣除掉很多无法避免的时间后,我只能算是勉勉强强看了下来。但是后面部分对我来说是真正陌生的,看起来理解慢,内容新,难度又大。实在是无法对自己寒假的学习感到满意。我觉得这真的需要认真反省。有的部分看起来反反复复看,也没有什么真正学到的,真正记在脑子里的。我想这对于没有Java基础的同学们来说学起来可能要更加困难。因此还需要花更多的时间来精读这本教材。通过对以前学过部分的Java资料对比,我发现这本教材讲的很详细,也很简明易懂。有很多代码可以借助理解。这正是我对读这本书的动力所在。我总感觉这本书的讲解风格很适合我来学习新的知识。因此,我会在和新同学们一起学习的过程中再去巩固自己的知识。