Java 基础总结(二)

Java 基础知识总结

三、面向对象

面向对象的三(四)大特性

封装:封装是指把描述一个对象的属性和行为的方法封装在一个类中,实现高内聚,低耦合,防止产生依赖的影响。

继承:继承是指在定义或实现一个类的时候,可以在一个已存在的类的基础之上来进行,把这个已存在的类所定义的内容作为自己的内容,并可以加入若干新内容,或修改原来的方法,使其更符合本身需求,这就是继承。继承是子类共享父类数据和方法的机制,这是类之间的关系,提高了软件的可用性和可扩展性。

多态:多态是指父类或接口定义的引用变量指向子类或具体实现类的实例对象,多态就是一个引用,多种实现方式。

抽象:抽象是找出一些事物的共性,将这些共性归为一个类,这个类只考虑这些事物的共性之处。

继承

在Java中使用extends关键字来标识两个类的而继承关系。

在子类中可以连同初始化父类构造方法来完成子类初始化操作,既可以在子类的构造方法中使用super()语句调用父类的构造方法,也可以在子类中使用super关键字调用父类的成员方法。但是子类没有权限调用父类中修饰符为private的方法,只能调用父类中修饰符为public或protected的成员方法

继承并不是只扩展父类的功能,还可以重写父类的成员方法。在继承中还有一种特殊的重写方式,子类与父类的成员方法返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊重写方式被称为重构。

注意:实例化子类对象时首先要实例化父类对象,然后再实例化子类对象。父类无参构造方法将被自动调用,但有参构造方法并不能被自动调用,只能依赖于supper关键字显示的调用父类的构造方法。

技巧:如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收对象占用内存时,对象的所有部分都能被正常终止(原因是父类已被实例化);

Object类

在Java中,Object类是一个特殊的类,它是所有类的父类,所有类都直接或间接继承了Object类,是Java类层中的最高层类。

1、getClass()方法

getClass()方法是Object类定义的方法,它会返回对象执行时的Class实例,然后使用此实例调用getName()方法取得类的名称。

2、toString()方法

toString()方法的功能是将一个对象返回为字符串形式,它会返回一个String实例。

3、equals()方法

equals()方法比较两个对象实际内容的方法

注意:Object类中的getClass()、notify()、notifyAll()、wait()等方法不可以被重写,因为这写方法被定义为分final类型。

多态

向上转型

把子类对象赋值给父类类型的变量,被称为向上转型。由于向上转型是从一个较具体的类到较抽象的类的转换,所以它总是安全的。

向下转型

通过向上转型可以推理出向下转型是将较抽象类转换为较具体的类,这样转型通常会出现问题。这个时候就需要将父类强制转换为某个子类对象,这种方式称为显示类型转换。

注意:当在程序中使用向下转型技术时,必须使用显示类型转换,向编译器指明将父类对象转换为哪一种类型的子类对象。

instanceof操作符

可以使用instanceof操作符判断是否一个类实现了某个接口,也可以使用instanceof来判断一个实例对象是否属于一个类。

在程序执行向下转型操作时,如果父类对象不是子类的实例,就会发生ClassCastException异常,所以在执行向下转型时,需要先判断是否为子类对象的实例。这个判断通常使用instanceof操作符来完成。

注意:instanceof是javau语言的关键字,在Java语言钟的关键字都为小写。

方法重载

方法重载就是在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数列表(参数个数,顺序,类型)不同即可

注意:虽然方法重载中可以使两个方法的返回值类型不同,但只有返回值类型不同并不足以区分两个方法的重载,所以方法重载与返回值类型无关。

方法重写

方法重写就是在子类中,可以有与父类相同方法名,相同返回值,相同参数列表的方法

注意:重写时,只能在子类中,父类方法的权限不能为private,子类方法抛出的异常与父类抛出的异常相同或是其子异常

抽象类

abstract是定义抽象类的关键字。

使用abstract关键字定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,如果声明一个抽象方法,那么承载这个抽象方法的类必须被标记为抽象类。

注意:抽象类被继承后需要实现其中所有的抽象方法,如果有某一抽象方法未被实现,那么该类仍需被定义为抽象类。

接口

接口使用interface关键字进行修饰。一个类实现一个接口可以用implements关键字。

注意:在接口中定义的方法必须被定义为public或abstract形式,其他修饰权限不被Java编译器认可。即使不声明,默认也为public。在接口定义的任何字段都自动是static和final的。

抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么?

1、抽象类和接口都不能被实例化
2、抽象类中可以没有抽象方法,也可以有具体的方法实现,而接口中有抽象方法,也可以有方法体(1.8新特性)
3、抽象类型需要被继承,接口需要被实现
4、抽象类只能单继承,接口可以多继承,并可以多实现
5、抽象类中可以有构造方法,静态方法,变量,常量;接口中只能有静态常量,只能并且默认是public staitc 
   final类型
6、抽象类中权限修饰符可以是public、protected(默认类型虽然eclipse不报错,但是应该也不行),接口 
   中权限修饰符只能是public,并且默认是public abstract 类型
7、接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发 
   挥作用,可以实现代码的重用

Java类包

Java中提供一种管理类文件的机制,就是类包。

类包不仅可以解决类名冲突问题,还可以在开发庞大的应用程序时,帮助开发人员管理庞大的应用程序组件,方便软件复用。同一包中的类互相访问时,可以不指定包名。

使用import关键字导入包。在使用import关键字时,可以指定类的完整描述,如果为了使用包中的更多类,可以使用import关键字指定时在包指定后加上*,这表示可以在程序中使用包中所有类。

使用import关键字导入静态成员。这是jdk 5.0以上版本提供的功能。语法为:import static 静态成员(路径)

注意:当使用import指定了一个包中的所有类时,并不会指定这个包的子包中的类,如果用到这个包中的子类,需要再次对子包做单独引用。

更改Java源文件位置

在Java中将源文件和类文件放在一起管理是几位不好的管理方式。可以在编译时使用-d参数设置编译后文件产生的位置。使用DOS进入程序所在的跟目录下,执行如下命令:javac -d ./bin ./com/lzw/*.java,这样编译成功后将当前运行路径下的bin目录中产生com/lzw路径,并在该路径下出现相应源文件的类文件。如果使用编译器(例:eclipse)并在创建项目时设置了源文件与输出文件的路径,编译后的类文件会自动保存在输出文件的路径中。

注意:如果不能在程序所在根目录下使用javac.exe命令,注意在path环境变量中设置Java编译器所在位置,假如是C:\Program Files\Java\jdk1.8.0_201\bin,可以使用set path=C:\Program Files\Java\jdk1.8.0_201\bin;%path%命令在DOS中设置path环境变量

final变量

final关键字用于变量声明,一旦该变量被设定,就不可以在改变该变量的值。通常被final修饰的变量称为常量。

final关键字定义的变量必须在声明时对其进行赋值操作。被定义为final的常量定义时需要使用大写字母名名,并且中间使用下划线链接,这是Java中编码命名规则。

final除了可以修饰基本数据类型常量,还可修饰对象引用。被定义为final的对象引用只能指向唯一一个对象,但是这个对象本身的值却是可以改变的。为了使一个常量真正做到不可被改变,可以将常量声明为static final。当常量被声明为static时,内存会为其开辟一块不能改变的存储空间。

final方法

被final修饰的方法不可以被重写如果一个父类的某个方法被设置为private修饰符,子类将无法访问该方法,自然无法覆盖该方法,所以一个定义为private的方法隐式被指定final类型,这样无须将一个定义为private的方法在定义为final类型。在父类中被定义为private final 的方法不可以被子类覆盖

final类

被final修饰的类不能被继承。如果将某个类设置为final形式,则类中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或非final形式。

异常

Java异常是Java提供的一种识别及响应错误的一致性机制。Java类库的每个包中都定义了异常类,所有异常类都是Throwable的子类。Throwable类派生了两个字类,分别是Exception和Error类。Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重。Exception类被称为非致命类,可以通过捕捉处理使程序继续执行。

Java异常机制用到的几个关键字:try、catch、finally、throw、throws

• try        -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch   -- 用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally  -- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有在finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw   -- 用于抛出异常。
• throws -- 用在方法签名中,用于声明该方法可能抛出的异常。

当try代码块发生异常时,程序就会跳转到catch代码块中执行,执行完catch代码块中的程序代码后,将继续执行catch代码块以后的其他代码,而不会执行try代码块发生异常语句后的代码。Java的异常处理是结构化的,不会因为一个异常而影响整个程序的执行。

finally不被执行的条件:

  1. 在finally语句块中发生了异常
  2. 在前面的代码中使用System.exit()退出程序
  3. 程序所在线程已经死亡
  4. 关闭CPU

Java中常见异常

常见的异常类
异常类 说明
ClassCastException 类型转换异常
ClassNotFoundEception 未找到类异常
ArithmeticException 算数异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ArrayStoreException 数组中包含不兼容的值异常
SQLException 操作数据库异常
NullPointerException 空指针异常
NoSuchFieldException 字段未找到异常
NoSuchMethodException 方法未找到异常
NumberFormatException 字符串转换为数字抛出异常
NegativeArraySizeException 数组元素为负数抛出异常
StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
IOException IO输入输出异常
IllegalAccessException 不允许访问某类异常
InstantiationException 当应用程序试图使用Class类中的new instance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出异常
EOFException 文件已结束异常
FileNotFoundException 文件未找到异常

自定义异常

只需要继承Exception类即可自定义异常。

  1. 创建自定义异常类。
  2. 在方法中通过 throw 关键字抛出异常对象。
  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句块捕获并处理,否则在方法的声明出通过throws关键字指明要抛出给方法调用者的异常。
  4. 在出现异常方法的调用者中捕获并处理异常。

throws(声明在方法体上)

throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常。多个异常可以使用逗号分割。

使用throws关键字将异常抛给上一级,如果不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的代码。

注意:如果是Error、RuntimeException或他们的子类,可以不使用throws关键字来声明要抛出的异常,编译仍然能成功通过,但在运行时会被系统抛出。

throw(声明在方法体中)

throw关键字用于方法体中,并抛出一个异常对象。程序执行到throw语句立即终止,它后面的语句不会再被执行。throw通常用来抛出用户自定义异常。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出的异常方法中使用throws关键字在方法声明中指明要抛出的异常;如果要捕获throw抛出的异常,则必须使用try-catch语句块。

RuntimeException异常

RuntimeException异常是程序运行过程中产生的异常。Exception类根据错误发生的原因分为RuntimeException和非RuntimeException类。

RuntimeException异常的种类
种类 说明
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ArithmeticException 算数异常
ArrayStoreException 数组中包含不兼容的值异常
IllegalArgumentException 非法参数异常
SecurityException 安全性异常
NegativeArraySizeException 数组元素为负数抛出异常

异常的使用原则

Java中异常强制用户去考虑程序的强健性和安全性。异常的处理不应该控制程序的正常流程,其主要的作用是捕获程序在运行时发生的异常并进行相应的处理。编写代码处理某个方法可能出现的异常时,可以遵循一下几条原则:

  1. 在当前方法中使用try-catch捕获异常
  2. 一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或异常的子类
  3. 如父类方法抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新异常

集合

java.util包中提供了一些集合类,这些集合类又被称为容器。常用的集合有List集合、Set集合和Map集合,其中List和Set继承了Collection接口。

Java 基础总结(二)_第1张图片

集合与数组的区别

  1. 数组的长度是固定的,集合的长度是可变的
  2. 数组用来存放基本数据类型,集合可用来存放对象的引用

Collection接口

Collection接口是层次结构中的根元素。构成Collection的单位称为元素。Collection接口通常不能直接使用,但改接口提供了添加元素、删除元素、管理数据的方法。由于List接口与Set接口都继承了Collection接口,因此这些方法对List和Set集合是通用的。

Collection接口中常用方法
方法 功能描述
add(E e) 将指定的对象添加到该集合中
remove(Object o) 将指定的对象从该集合移除
isEmpty() 返回Boolean值,用于判断当前集合是否为空
iterator 返回在此Collection的元素上进行迭代的迭代器。用于遍历集合中的对象
size() 返回int型值,获取该集合中元素个数

Iterator遍历迭代器

通常遍历集合,都是通过迭代器(Iterator)来实现。Collection接口中的iterator()方法可返回在此Collection进行进行迭代的迭代器。下面列举一个典型的遍历集合的方法。

import java.util.*;

public class Muster {

    public static void main(String args[]){
    
        Collection list = new ArrayList;
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator it = list.iterator();
        while(it.hasNext()){
            String str = (String)it.next();
            System.out.println(str);
        }
    }
}

注意:Iterator的next()方法返回的是Object

List集合

List结合包括List接口以及List接口的所有实现类。Lsit集合中的元素允许重复,各元素的顺序就是对象插入的顺序。类似Java数组,用户可以通过使用索引(元素所在集合中的位置)来访问集合中的元素。

List接口继承了Collection接口,因此包含了Collection中所有的方法,此外,List接口还定义了两个非常重要的方法:

  1. get(int index); // 获取指定索引位置的元素
  2. set(int index,Object obj); // 将集合中指定索引位置的对象修改为指定的对象

List接口的常用实现类有ArrayList与LinkedList。

ArrayList类实现了可变的数组,允许保存所有元素,包括null,并可根据索引位置对集合进行快速的随机访问;缺点是向指定的索引位置插入或删除对象时速度较慢。相对于LinkedList而言,查询、修改较快插入、删除较慢

LinkedList类采用链表结构保存对象。这种结构的优点是便于向集合中插入和删除对象,但随机查询集合中的对象效率较低。相对于ArrayList而言,插入、删除较快,查询、修改较慢

说明:与数组相同,集合的索引(下标)也是从0开始。

Set集合

Set集合中的对象不按特定方式排序,只是简单地把对象加入集合中,但Set集合中不能包含重复对象。Set集合有Set接口和Set接口实现类组成。Set接口继承了Collection接口,因此包含了Collection接口的所有方法。

Set接口常用的实现类有HashSet类和TreeSet类。

HashSet类实现了Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证Set的迭代顺序,特别是它不保证该顺序永恒不变。此类允许使用null元素。

TreeSet类实现了Set接口,还实现了java.util.SortedSet接口。因此,TreeSet类实现的Set集合在遍历集合时按照自然顺序递增排序,也可以按照指定比较器递增排序,即可以通过比较器对用TreeSet类实现的Set集合中的对象进行排序。

TreeSet类增加的方法
方法 功能描述
first() 返回此Set中当前第一个(最低)元素
last() 返回此Set中当前最后一个(最高)元素
comparator() 返回对此Set中的元素进行排序的比较器。如果此Set使用自然排序,则返回null

headSet(E toElement)

返回一个新的Set集合,新集合是 toElement(不包含)之前的所有对象
subSet(E fromElement,E toElement) 返回一个新的Set集合,是 fromElement(包含)对象与 toElement(不包含)对象之间的所有对象
tailSet(E fromElement) 返回一个新的Set集合,新集合包含对象 fromElement(包含)之后的所有对象

技巧:headSet()、subSet()、tailSet()方法截取对象生成新集合时是否包含指定的参数,可通过如下的方法来判别:

  1. 如果指定参数位于新集合的起始位置,则包含该对象,如subSet()方法的第一个参数和tailSet()方法的参数
  2. 如果指定参数是新集合的终止位置,则不包含该参数,如headSet()方法的入口参数和subSet()的第二个参数

注意:Set的构造有一个约束条件,传入的Collection对象不能有重复值,必须小心操纵可变对象。

Map集合

Map集合没有继承Collection接口,其提供的是key到value的映射。Map中不能包含相同的key,每个key只能映射一个value。key还决定了存储对象在映射中的存储位置,但不是由key对象本身决定的,而是通过一种“散列技术”进行处理,产生一个散列码的整数值。散列码通常用作一个偏移量,该偏移量对应分配给映射的内存区域的起始位置,从而确定存储对象在映射中的存储位置。Map集合包括Map接口以及Map接口的所有实现类。

Map接口中常用的方法
方法 功能描述
put(K key, V value) 向集合中添加指定的key与value的映射关系
containsKey(Object key) 如果此映射包含指定key的映射关系,则返回true

containsValue(Object value)

如果此映射将一个或多个key映射到指定值,则返回true
get(Object key) 如果存在指定key的对象,则返回该对象对应的值,否则返回null
keySet() 返回该集合中的所有key对象形成的set集合
values()

返回该集合中所有值对象形成的Collection集合

说明:Map集合中允许值对象是null,而且没有个数限制

Map接口常用的实现类由HashMap和TreeMap。

HashMap类基于哈希表的Map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和null键,但必须保证键的唯一性。HashMap通过哈希表对其内部的映射关系进行快速查找。此类不保证映射顺序,特别是它不保证该顺序恒久不变。

TreeMap类不仅实现了Map接口,还实现了java.util.SortedMap接口,因此集合中的映射关系具有一定的顺序。但在添加、删除和定位映射关系时,TreeMap类要比HashMap类性能要差。由于TreeMap类实现的Map集合中的映射关系是根据键对象按照一定顺序排列的,因此不允许键对象是null。

可以通过HashMap类创建Map集合,当需要顺序输出时,在创建一个完全相同映射关系的TreeMap类实例。

建以使用HashMap类实现Map集合,因为由HashMap类实现的Map集合添加和删除映射关系效率更高。HashMap是基于哈希表的Map接口的实现,HashMap通过哈希码对其内部的映射关系进行快速查找;而TreeMap中的映射关系存在一定的顺序,如果希望Map集合中的对象也存在一定的顺序,应该使用TreeMap类实现集合。

I/O输入输出

流是一组有序的数据序列,根据操作的类型,可分为输入流输出流两种。I/O(Input/Output,输入/输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。虽然I/O流通常与磁盘文件存储有关,但是程序的源和目的地也可以是键盘,鼠标,内存或显示器窗口等。

Java由数据流处理输入/输出模式,程序从指向源的输入流中读取源中的数据。源可以是文件、网络、压缩包或其他数据源。输出流的指向是数据要到达的目的地,程序通过向输出流中写入数据把信息传递到目的地。输出流的目标可以是文件、网络、压缩包、控制台和其他数据输出目标。

Java语言定义了许多类专门负责各种方式的输入/输出,这些类都被放在java.io包中。其中,所有输入流类都是抽象类InputStream(字节输入流)或抽象类Reader(字符输入流)的子类;而所有输出流都是抽象类OutputStream(字节输出流)或抽象类Write(字符输出流的子类)。

输入流

InputStream类是字节输入流的抽象类,是所有字节输入流的父类。该类中所有方法遇到错误时都会引发IOException异常。

  1. read()方法:从输入流中读取数据的下一字节。返回0-255范围的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1
  2. read(byte[] b):从输入流中读入一定长度的字节,并以整数的形式返回返回字节数
  3. mark(int readlimit):在输入流的当前位置放置一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数
  4. reset():将输入指针返回到当前所作标记的标记处
  5. skip(long n):跳过输入流上的n个字节并返回实际跳过的字节数
  6. marksupported():如果当前流支持mark()/reset()方法,就会返回返回true
  7. close():关闭此输入流并释放与该流关联的所有系统资源

说明:并不是所有的InputStream类的子类都支持InputStream中定义的所有方法,如skip()、mark()、reset()等方法只对某些子类有用。

Java中的字符是Unicode编码,是双字节的。InputStream是用来处理字节的,并不适合处理字符文本。Java为字符文本的输出专门提供了一套单独的类Reader,但Reader类并不是InputStream的替换者,只是在处理字符串时简化了编程。Reader类是字符输入流的抽象类,所有字符输入流的实现都是它的子类。

输出流

OutputStream类是字节输出流的抽象类,是所有字节输出流的父类。该类中所有方法均返回void类型,遇到错误时都会引发IOException异常。

  1. write(int b):将指定的字节写入此输出流
  2. write(byte[] b):将b个字节从指定的byte数组写入此输出流
  3. write(byte[] b,int off,int len):将指定的byte数组中从偏移量off开始的len个字节写入此输出流
  4. flush():彻底完成输出,并清空缓存区
  5. close():关闭输出流

Writer类是字符输出流的抽象类,所有字符输出类的实现都是它的子类。

File类

File类的java.io包中唯一代表磁盘文件本身的对象。File类定义了一些与平台无关的方法来操作文件,可以通过调用File类中的方法,实现创建、删除、重命名等操作。File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件长度、文件读写权限等。数据流可以将数据写入到文件中,文件也是数据流最常用的数据媒体。

文件的创建与删除

可以使用File类创建一个文件对象,通常使用以下3中构造方法创建文件对象。

  1. File(String pathname):pathname为文件路径名称(包括文件名和后缀)
  2. File(String parent,String child):该构造方法根据定义的父路径和子路径(包含文件名和后缀)创建一个新的File对象
  3. File(File f,String child):该构造方法根据定义的f抽象路径名和child路径名字符串创建一个新File实例
File类的常用方法
方法 返回值 说明
getName() String 获取文件名称
canRead() boolean 判断文件是否为可读
canWrite() boolean 判断文件是否为可被写入
exits() boolean 判断文件是否存在
length() long 获取文件长度(以字节为单位)
getAbsolutePath() String 获取文件的绝对路径
getParent() String 获取文件的父路径
isFile() boolean 判断文件是否存在
isDirectory() boolean 判断文件是否为一个目录
isHidden() boolean 判断文件是否为隐藏文件
lastModified() long 获取文件最后修改时间

文件输入/输出流

FileInputStream类和FileOutputStream类都用来操作磁盘文件。FileInputStream继承自InputStream,FileOutputStream继承自OutputStream。

FileInputStream常用的构造方法如下:

  1. FileInputStream(String pathname)
  2. FileInputStream(File f)

第一个构造方法使用给定的文件名name(完整路径)创建一个FileInputStream对象,第二个构造方法使用File对象创建FileInputStream对象。

FileOutputStream类有与FileInputStream类相同参数的构造方法,创建一个FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已经被其他应用程序打开的文件。

注意:虽然Java在程序结束时自动关闭所有打开的流,但是当使用完流后,显式的关闭所有打开的流仍是一个好习惯。一个被打开的流有可能会用尽系统资源,这取决于平台和实现。如果没有将打开的流关闭,当另一个程序试图打开另一个流时,可能会得不到需要的资源。

FileReader类与FileWriter类

使用FileInputStream类从文件中读取内容与使用FileOutputStream像文件中写入数据,都有一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占用2个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。

FileReader和FileWriter字符流对应了FileInputStream和FileOutputStream类。FileReader流顺序的读取文件,只要不关闭流,每次调用read()方法就能顺序读取源中其余内容,直至源的末尾或流被关闭。

带缓存的输入/输出流(BufferInputStream/BufferOutputStream)

缓存是I/O的一种性能优化。缓存流为I/O流增加了内缓存区。有了缓存区,使得在流上执行skip()、mark()和reset()方法都成为可能。

BufferInputStream类可以对所有InputStream类进行带缓存区的包装以达到性能的优化。BufferInputStream有两个构造方法:

  1. BufferInputStream(InputStream in)
  2. BufferInputStream(InputStream  in,int size)

第一种构造方法创建了一个带有32个字节的缓存流;第二种形式的构造方法按指定的大小来创建缓存区。

使用BufferOutputStream输出信息和用OutputStream输出信息完全一样,只不过BufferOutputStream有一个flush()方法将缓存区的数据强制输出完。BufferOutputStream有两个构造方法:

  1. BufferOutputStream(OutputStream out)
  2. BufferOutputStream(OutputStream out,int size)

第一种构造方法创建了一个带有32个字节的缓存流;第二种形式的构造方法按指定的大小来创建缓存区。

注意:flush()方法就是用于即使缓存区没有满的情况下,也将缓存区的内容强制写入到外设,习惯上称这步操作为刷新。flush()方法只对使用缓存区的OutputStream类的子类有效。当调用close()方法时,系统在关闭流之前,也会将缓存区中的信息全部刷新到磁盘文件中。

BufferReader与BufferWriter类

BufferReader类与BufferWriter类分别继承了Reader类与Writer类。这两个类同样具有内部缓存机制,并可以以行为单位进行输出。

BufferReader类常用的方法如下:

  1. read():读取单个字符
  2. readLine():读取一个文本行,并将其返回为字符串。若无数据可读,则返回null

BufferWriter类常用的方法如下:

  1. writer(String s,int off,int len):写入字符串的某一部分
  2. flush():刷新该流的缓存
  3. newLine():写入一个行分隔符

注意:在使用BuffeWriter类的write()方法时,数据并没有立即被写入输出流,而是首先进入缓存区中。如果想立即将缓存区中的数据写入输出流,一定要调用flush()方法。

数据输入输出流(DataInputStream/DataOutputStream)

数据输入/输出流(DataInputStream/DataOutputStream)允许应用程序以与机器无关的方式从底层输入流中读取基本的Java数据。也就是说,当读取一个数据时,不必在关心这个数值应当是哪种字节。

DataInputStream类与DataOutputStream类构造方法如下:

  1. DataInputStream(InputStream in)
  2. DataOutputStream(OutputStream out)

DataOutputStream类提供了如下3种写入字符串的方法:

  1. writeBytes(String s)
  2. writeChars(String s)
  3. writeUtf(String s)

由于Java中字符使用的是Unicode编码,是双字节的,writeBytes只是将字符串中的每一个字符的低字节内容写入目标设备中;而writeChars将字符串中的每一个字符的两个字节的内容都写到目标设备中;writeUtf将字符串按照UTF编码后的字节长度写入目标设备,然后才是每一个字节的UTF编码。

DataInputStream类只提供了一个readUTF()方法返回字符串。这是因为要在一个连续的字节流读取一个字符串,如果没有特殊的标记作为一个字符串的结尾,并且不知道这个字符串的长度,就无法知道读取到什么位置才是这个字符串的结束。DataOutputStream类中只有writeUTF()方法向目标设备中写入字符串长度,所以也能准确地读回写入字符串。

ZIP压缩输出/输入流

ZIP压缩管理文件(ZIP archive)是一种十分典型的文件压缩形式,使用它可以节省存储空间。使用java.util.zip包中的ZipOutputStream与ZipInputStream类来实现文件的压缩/解压缩。如果要从ZIP压缩管理文件内读取某个文件,首先要找到对应文件的“目录进入点”(从它可知该文件在ZIP文件内的位置),才能读取这个文件的内容。

Java实现l/O数据流与网络数据流单一接口,因此数据的压缩、网络传输和解压缩的实现比较容易。ZipEntry类参生的对象,是用来代表一个ZIP压缩文件内的进入点(entry)。ZipInputStream类用来读取ZIP压缩格式的文件,所支持的包括已压缩及未压缩的进入点(entry)。ZipOutputStream类用来写出ZIP压缩格式的文件,所支持的包括已压缩及未压缩的进入点(entry)。

利用ZipOutputStream类对象,可将文件压缩为.zip文件。ZipOutputStream的构造方法为:ZipOutputStream(OutputStream out);

ZipOutputStream类常用方法
方法 返回值 说明
putNextEntry(ZipEntry e) void 开始写一个新的ZipEntry,并将流内的位置移至此entry所指数据开头
write(byte[] b,int off,intlen) void 将字节数组写入当前ZIP条目数
finish() void 完成写入ZIP输出流的内容,无须关闭它所配合的OutputStream
setComment(String comment) void 可设置此ZIP文件的注释文字

ZipInputStream类可读取ZIP压缩格式的文件,包括已压缩和未压缩的条目(entry)。ZipInputStream的构造方法为:ZipInputStream(InputStream in)。

ZipInputStream类常用方法
方法 返回值 说明
read(byte[] b,int off,int len) int 读取目标b数组内off偏移量的位置,长度是len字节
available() int 判断是否已经读完目前entry所指的数据,以读完返回0,否则返回1
closeEntry() void 关闭当前ZIP条目并定位流,以读取下一条目
skip(long n) long 跳过当前ZIP条目中指定的字节数
getNextEntry() ZipEntry 读取下一个ZipEntry,并将流内的位置移至该entry所指数据的开头
createZipEntry(String name) ZipEntry 已制定的name参数新建一个ZipEntry对象

注意:使用ZipInputStream类来解压文件,必须先使用ZipInputStream类的getNextEntry()方法来取得其内的第一个ZipEntry。

枚举

enum是定义枚举类型的关键字。

枚举类型常用的方法
方法名 具体含义 使用方法 举例
values() 该方法可以将枚举类型成员以数组的形式返回 枚举类型名称.values() Constants.values()
valueOf() 该方法可以实现将普通字符串转为枚举实例 枚举类型名称.valueOf("abc") Constants.valueOf("abc")
compareTo() 该方法用于比较两个枚举对象在定义时的顺序 枚举对象.compareTo() Constants.
compareTo(Constants1)
ordinal() 该方法用于得到枚举成员的位置索引 枚举对象.ordinal() Constants.ordinal()
  1. values(),该方法将枚举类型的成员变量实例以数组的形式返回,也可以通过该方法获取枚举类型的成员。
  2. valueOf()与compareTo(),valueOf()可以将普通字符串转换成枚举类型,而compareTo()方法用于比较两个枚举类型对象定义时的顺序。compareTo()返回值为正值时,代表方法中参数在调用该方法的枚举对象位置之前;0代表两个互相比较的枚举成员成员位置相同;负值代表方法中参数在调用该方法的枚举对象位置之后。
  3. 枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值。

在枚举中可以添加构造方法,但是规定这个构造方法必须为private修饰符所修饰。

枚举类型的优势:

  1. 类型安全
  2. 紧凑有效的数据定义
  3. 可以和程序其他部分完美交互
  4. 运行效率高

总结:

  1. 枚举型可以直接与数据库打交道,我通常使用varchar类型存储,对应的是枚举的常量名。(数据库中好像也有枚举类型,不过也没用过)
  2. switch语句支持枚举型,当switch使用int、String类型时,由于值的不稳定性往往会有越界的现象,对于这个的处理往往只能通过if条件筛选以及default模块来处理。而使用枚举型后,在编译期间限定类型,不允许发生越界的情况
  3. 当你使用常量类时,往往得通过equals去判断两者是否相等,使用枚举的话由于常量值地址唯一,可以用==直接对比,性能会有提高
  4. 常量类编译时,是直接把常量的值编译到类的二进制代码里,常量的值在升级中变化后,需要重新编译引用常量的类,因为里面存的是旧值。枚举类编译时,没有把常量值编译到代码里,即使常量的值发生变化,也不会影响引用常量的类。
  5. 枚举类编译后默认为final class,不允许继承可防止被子类修改。常量类可被继承修改、增加字段等,容易导致父类的不兼容。

泛型

Object类为最上层父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。需要使用这些实例时,必须正确的将该实例转换为原来的类型,否则会返回ClassCastException异常。

在JDK1.5之后,提出泛型机制。类名,T代表一个类型的名称。使用泛型定义的类在声明该类对象时可以根据不同的需求指定真正的类型,而在使用类中的方法传递或返回数据类型时将不再需要进行类型转换操作,而是使用声明在声明泛型对象时"<>"符号中设置的数据类型。使用泛型这种形式将不会方式ClassCastException异常,因为在编译器中就可以检查类型匹配是否正确。

  1. 定义泛型类时声明多个类型
    1. MutiOverClass
    2. MutiOverClass:泛型类名称
  2. 定义泛型类时sheng'm数组类型:可以使用泛型机制声明一个数组,但是不可以使用泛型来建立数组实例。可以通过set方法赋值。
  3. 集合类声明容器元素
常用的泛型化集合l类
集合类 泛型定义
ArrayList Arraylist
HashMap HashMap
HashSet HashSet
Vector Vector

限制泛型可用类型

class 类名称 :其中anyClass指某个接口或类,代表泛型类的类型必须实现或继承了anyClass这个类或接口。无论anyClass是接口还是类,在进行泛型限制时都必须使用extends关键字。

泛型类名称 = null:其中 表示类型未知,当需要使用该类型时,可以单独实例化。要在创建一个泛型类对象时限制这个泛型类的类型实现或继承某个接口或类的子类,可以使用”?“通配符来表示,同时使用extends关键字来对泛型加以限制。

注意:使用通配符声明的名称实例化的对象不能对其加入新的信息,只能获取或删除。

延申:泛型类型限制除了向下限制外,还可以进行向上限制,只要在定义的时候使用 super 关键字即可。例如:"A = nul",这样定义后,对象a只接受List接口或上层父类类型。 

定义为泛型的类和接口也可以被继承与实现。

如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用extend ExtendsClass语句进行继承操作,则SubClass类中的T类型都会自动变成Object,所以一般情况下都是将父类的泛型类型保留。

泛型总结

  1. 泛型的类型参数只能是类类型,不可以时简单类型,如A这种泛型定义就是错误的
  2. 泛型的类型个数可以是多个
  3. 可以使用extends或super关键字限制泛型的类型
  4. 可以使用通配符限制泛型的类型

你可能感兴趣的:(Java)