容器(3) + IO(1)

复习

1.ListIterator 是Iterator 的子接口,是专门用于遍历List 容器的迭代器,可以实现双向的遍历。
2.HashSet 元素无序,唯一,可以有一个null。底层使用HashMap 实现,只使用了HashMap的key 部分。Value是一个final static Object 对象。
3.TreeSet 元素有序(升序),唯一,底层使用TreeMap实现,只使用了TreeMap的key 部分。元素的有序是通过 内部比较器 java.lang.Comparable或者外部比较器java.util.Comparator 进行添加元素的时候的比较,优先使用 java.util.Comparator.
4.HashMap : 中保存的是键值对对象 Entry。底层使用哈希表实现。元素无序,唯一的。Key可以有一个null,value可以有多个null。Key要求是唯一的,无序的。整个Entry在哈希表中的位置key 的哈希码 决定。为了保证 key 的唯一性和无序性,那么key的对应的类型中需要重写hashCode 和 equals 方法。Key 的哈希码决定了整个Entry 在哈希表中的位置,equals保证了所有的Entry 的key 是唯一的。
5.HashMap 的遍历。得到所有的key通过 keySet();得到所有的value通过values()方法;得到所有的Entry通过entrySet()方法。

第一节 LinkedHashMap、TreeMap

1.LinkedHashMap

该类是HashMap的子类,HashMap的元素是无序(添加和遍历的顺序不一致)的,该类在原有的基础上增加了一个单独的链表,用来维护元素添加的顺序,遍历的顺序和添加的顺序一致了。遍历元素的效率和HashMap基本一致,但是修改元素的效率低了,因为要维护两个数据结构。

2.TreeMap

特点:底层使用二叉树实现,每一个节点中包含了key+value。节点的Key 是有序的(升序),唯一的,不能是null,value可以有多个null。value和对应的key是绑定的,key 的有序性是通过内部比较器或外部比较器实现的。根据内容效率介于HashMap 和 ArrayList之间。

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 *TreeMap 的学习
 */
public class TestTreeMap {
    public static void main(String[] args) {
        asc();
        deasc();
    }
    //升序
    static void asc(){
        Map<String,String> map=new TreeMap();
        map.put("cn","China");
        map.put("jp","Japan");
        map.put("us","America");
        map.put("uk","United Kingdom");
        map.put("fr","France");
        map.put("uk","England");
        System.out.println(map);
    }
    //降序
    static void deasc(){
        Comparator<String> com=new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        };
        Map<String,String> map=new TreeMap(com);
        map.put("cn","China");
        map.put("jp","Japan");
        map.put("us","America");
        map.put("uk","United Kingdom");
        map.put("fr","France");
        map.put("uk","England");
        System.out.println(map);
    }
}

第二节 其他的容器类、泛型

1.其他的容器类

1.Java.util.Hashtable 底层使用数据结构为哈希表,存储的是键值对 对象。功能基本和 HashMap一致。Hashtable 是早期的一个容器类,是一个线程安全的,效率相对较低的容器。HashMap 是线程非安全的,它的出现是为了在安全的情况下实现对Hashtable 的替代。Hashtable 的key 无序的,唯一的,但是key 和 value 都不能是null. Hashtable 早期的遍历的方式,得到所有的key的方法,和得到所有的value的方法,使用枚举器 进行遍历。
2.IdentifyHashMap :和 HashMap的唯一区别是,该容器中元素的 key ,只要key是一个区别于其他key 的key(比较的不是内容,而是地址),只要两个key 的地址不同,就认为是不同的key。
3.WeakHashMap :强引用、弱引用、虚引用。
4.Properties:是Hashtable 的子类,该类用于处理配置文件,该类没有泛型,key和value 必须都是String,对中文支持的不好。

/**
 *HashTable和Property的学习
 */
public class Test {
    public static void main(String[] args) {
        test();
        test1();

    }
    static void test(){
        Hashtable hashtable=new Hashtable();
        //早期获得所有的key的方法
        Enumeration keys = hashtable.keys();
        //早期的获得所有values的方法
        Enumeration elements = hashtable.elements();
    }
    //jvm启动时会将系统中很多的信息加载到内存中。
    //可以通过System获得。
    static void test1(){
        //得到系统信息的键值对 容器对象
        Properties properties=System.getProperties();//是HashTable的子类
        Enumeration<Object> keys = properties.keys();
        while(keys.hasMoreElements()){
            String key=keys.nextElement().toString();
            String value=properties.getProperty(key);
            System.out.println(key+"-->"+value);
        }

    }

2.Java.uitl.Collections

学习完数据学习了一个用于处理数组的工具类java.util.Arrays
Java.util.Collections 是一个专门用于处理容器对象的工具类。包含了一些工具方法。

3.泛型

Jdk1.5推出内容。在容器中使用泛型,可以避免加入不该加入的元素类型,避免获得元素之后进行类型的强转。
泛型又称为参数化类型。
泛型可以在方法、类、接口中定义使用。
1.泛型方法

/定义一个方法,可以接收任意类型为实参,并返回该实参的类型。
public static Object test(Object o){
    return o;
}
/**
 * 泛型方法的定义。
 * <T> 是定义泛型方法的符号。T这个字符,只要是一个合法的标识符就行。
 * 一般使用T 代表 任意的类型 Type  E 表示元素的类型。
 * @param t
 * @param <T>
 * @return
 */
public static <T> T test1(T t){
    return t;
}
//方法中的泛型的上限下限的界定
//传入的实参 list 的元素的类型必须是 Number 类型或者是它的子类类型
//规定了泛型的上限
public static void test2(List<? extends Number> list){
}
//规定泛型的下限
// 容器的元素的泛型的类型必须是Number或者是Number 的父类类型
public static void test3(List<? super Number> list){

2.泛型类

//泛型类
class MyClass<T>{
    T t;

    T test(){
        return null;
    }
}

//设计类来描述栈
class MyStack<E>{
    //定义栈的初始容量
    public static final int DEFAULT_CAPACITY = 6;
    //底层使用数组实现
    private Object[] elementData;
    //栈顶指针
    private int index;
    public MyStack(){
        elementData = new Object[DEFAULT_CAPACITY];
    }
    //压栈操作
    public void push(E o){
        //栈满了。扩容,扩容的规则,1.5倍
        if(isFull()){
            //扩容
            elementData = Arrays.copyOf(elementData,elementData.length * 3 >> 1);
        }
        elementData[index++] = o;
    }

    public E pop()throws Exception{
        //栈空了
        if(isEmpty()){
            throw  new Exception("感觉身体被掏空!");
        }else{
            //指针下移,然后将指针指向的位置的数据获得
//            index --;
            E o = (E)elementData[--index];
            //避免内存泄露的
            elementData[index] = null;
            return o;
        }
    }

    //获得栈顶元素,但是不移除。
    public E peek()throws Exception{
        //栈空了
        if(isEmpty()){
            throw  new Exception("感觉身体被掏空!");
        }else{
            return (E)elementData[index-1];
        }
    }
    /**
     * 栈满了。
     * @return 满了,返回true,否则false。
     */
    private boolean isFull(){
        return index == elementData.length;
    }
    public boolean isEmpty(){
        return index == 0;
    }
}

3.泛型接口

//泛型接口
interface MyInterface<T>{
    T test();
}
//泛型接口的使用
//子类实现泛型接口的时候,指明泛型的具体类型。
class Student implements MyInterface<Student>{
    @Override
    public Student test() {
        return null;
    }
}
//子类实现泛型接口的时候,可以继续使用泛型。但是子类必须也使用泛型
class Stu<T> implements MyInterface<T>{
    @Override
    public T test() {
        return null;
    }
}

4. 泛型擦除

/**
 * 泛型信息的擦除问题
 * 对象的泛型的信息只在编译期有效,
 * 泛型的信息安全检测是在编译期进行的,
 * 到了运行期,所有的泛型的信息都将被擦除。
 * 字节码中不包含泛型的相关的 信息。
 * 以容器为例,运行期,元素的类型就不在做判断了。
 */
public class TestRemoveGen {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("abc");
//        list.add(1);
        //自动向上类型转换
        Object o = list;
        List l = (List)o;
        l.add(1);
        System.out.println(list);

        List<Integer> list1 = new ArrayList<>();

        //对象的类型信息的名字
        System.out.println(list.getClass().getName());
        System.out.println(list1.getClass().getName());
    }
}

4.容器总结

1.哪些类可以使用迭代器遍历,实现了Iterable 接口的类都可以。List+Set
2.Collection 元素特点: 无序,不唯一,可以有多个null
3.List 继承了Collection 元素特点:有序,不唯一,可以有多个null。
4.ArrayList 是List 的子类,元素特点:有序,不唯一,可以有多个null。底层使用数组实现。是一个性能优良的容器类。
5.LinkedList 是List 的子类。元素特点 有序,不唯一,可以有多个null。底层使用双向链表实现。头部和尾部元素的添加和删除效率比较高。
6.ArrayList 和 LinkedList 的相同点和不同点。
7.ArrayList 和 Vector 的异同。
8.Set 元素特点:无序,唯一,单个null
9.HashSet 底层使用HashMap实现。
10.TreeSet底层使用TreeMap实现。
11.HashMap 底层使用哈希表实现,元素无序,key 是无序,唯一,单个null。Key的类型中需要重写hashCode 和equals。Value 无序,不唯一,多个null。
12.Object 类的hashCode 的默认实现。
13.TreeMap,底层使用二叉树实现,key 是有序的,升序,使用内部外部比较器进行排序比较。Key不能是null。
14.HashMap 和 Hashtable 的异同。
15.泛型方法、泛型类、泛型接口。

第三节 IO概述

1.IO 相关的概念

IO 指的是 Input 和 Output,输入输出。
概念:流 stream。是一个信息的通道,通过该通道可以实现对数据源的读写操作。
比较抽象的概念:对操作的数据源的抽象的表示形式。
可以通过IO流实现对文件的读写操作。

2.IO流的分类

1:根据数据的流向(参照程序内存)
输入流:将外部的数据源的数据可以读取到内存中的流。InputStream、Reader
输出流:将内存中的数据可以写到外部数据源的流。OutputStream、Writer
2:根据处理的数据单元
字节流:流处理数据的时候以字节为基本单位的流。InputStream、OutputStream
字符流:流处理数据的时候以字符为基本的单位的流。Reader、Writer
字符流只能对纯文本数据进行读写,字节流任何数据都可以,只不过字符流在处理字符数据上优势比较明显。
3:根据处理的数据的源头不同
节点流:直接以数据源为源头的流。FileInputStream,FileOutputStream。
处理流:以其他的流为数据源的流。BufferedInputStream BufferedOutputStream

3.IO的继承的体系

1.InputStream 是所有的字节输入流的父类,是一个抽象类。
2.OutputStream 是所有的字节输出流的父类,是一个抽象类。
3.Reader 是所有的字符输入流的父类,是一个抽象类。
4.Writer 是所有的字符输出流的父类,是一个抽象类。

第四节 文件字节字符流

1.文件字节输入输出流

FileInputStream:读取字节文件数据的。文件字节输入节点流。
FileOutputStream:对字节文件数据写入的。文件字节输出节点流。

import java.io.*;

/**
 * FileInputStream 读取字节文件数据的,文件字节输入流
 * FileOutputStream 对字节文件数据写入的,文件字节输出流
 */
public class TestFileStream {
    public static void main(String[] args) throws IOException {
        //创建一个文件
        //new File("1.txt").createNewFile();
        //readFile();
        //readFile1();
        readFile2();
        //copyFile(new File("e:/1.png"),new File("e:/1_copy.png"));

    }
    //使用FileStream读取指定文件的数据
    static void readFile(){
        File file=new File("1.txt");
        FileInputStream fis=null;
        try {
            //在程序和数据源之间搭建流对象,信息的通道
            fis=new FileInputStream(file);
            fis.skip(6);//跳过指定的字节数,从后继续读取
            //通过流对象读取数据源的信息
            //从数据源中读取下一个字节,并返回该字节
            //如果数据源中没有返回数据返回-1;
            int value=fis.read();
            while(value!=-1){
                System.out.print((char)value);
                value=fis.read();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    //流对象在使用完毕之后必须关闭,不然GC回收不了
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //
    static void readFile1(){
        File file=new File("1.txt");
        FileInputStream fis=null;
        try {
            fis=new FileInputStream(file);
            byte[] buf=new byte[10];
            int count=0;
            while((count=fis.read(buf))!=-1){
                String str=new String(buf,0,count);
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
    //去乱码的
    static void readFile2(){
        File file=new File("1.txt");
        FileInputStream fis=null;
        try {
            //得到文件的字节数
            //long length=file.length();
            //查看流对象中可读的字节数
           // fis.available();
            long length=fis.available();
            fis=new FileInputStream(file);
            //创建与文件大小相同的字节数组
            byte[] buf=new byte[(int)length];//注意数组的长度是int类型这里要将long类型强制转化为int类型
            fis.read(buf);
            String str=new String(buf);
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    //流对象使用完毕之后,不许关闭不然GC回收不了
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //将指定的字符串写到指定的文件中
    public static void writeFile(){
        FileOutputStream fos=null;
        try {
            fos=new FileOutputStream("2.txt");
            //将字符写入文件
            fos.write(65);//A
            fos.write('\n');//
            fos.write(97);//a
            fos.write('\n');
            String str="千万里我追寻着你";
            //编码的过程
            byte[] bytes=str.getBytes();
            fos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }

    //使用FileInputStream和FileOutputStream实现文件的复制
    public static void copyFile(File srcFile,File destFile){
        FileInputStream fis=null;
        FileOutputStream fos=null;
        try {
            fis=new FileInputStream(srcFile);
            fos=new FileOutputStream(destFile);
            byte[] buf=new byte[1024];
            int count=fis.read(buf);
            while(count!=-1){
                fos.write(buf,0,count);
                count=fis.read(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //关于FileOutputStream 的问题
    static void test1() throws Exception{
        //使用下面的构造方法创建的fos对象,当代码一执行
        //会将数据源清空。然后再讲写入的内容写入。
        //如果想实现文件数据的尾部添加,那么就添加第二个参数。true代表 尾部追加
        //false 代表先清空再追加。
        FileOutputStream fos=new FileOutputStream("2.txt",true);

        fos.write("你好么".getBytes());//追加写入
        fos.close();
    }
}

2.文件字符输入输出流

/**
 * FileReader:从文本数据读取字符
 * FileWrite:向文本写入字符
 */
public class TestFileReadWrite {
    public static void main(String[] args) {
        copyFile("e:/a.txt","e:/a_copy.txt");
    }
    //从指定的文件中读取字符数据
    static void readFile(File file){
        FileReader fr=null;
        try {
            fr=new FileReader(file);
            //读取一个字符数据带到末尾返回-1
            int value=fr.read();
            while(value!=-1){
                System.out.println((char)value);
                value=fr.read();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fr!=null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //文本复制
    static void copyFile(String srcPath,String destPath){
        FileReader fr=null;
        FileWriter fw=null;
        try {
            fr=new FileReader(srcPath);
            fw=new FileWriter(destPath);
            char[] buf=new char[10];
            //从数据源一次读取多个字符数据,放到buf中,并返回本次读取到的有效的字符个数
            int count=fr.read(buf);
            while(count!=-1){
                fw.write(new String(buf,0,count));
                count=fr.read(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
    //文本复制
    static void copyFile1(String srcPath,String destPath) throws Exception{
        FileReader fr=new FileReader(srcPath);
        FileWriter fw=new FileWriter(destPath);
        char[] buf=new char[10];
        //从数据源一次读取多个字符数据,放到buf中,并返回本次读取到的有效字符个数
        int count=fr.read(buf);
        while(count!=-1){
            fw.write(new String(buf,0,count));
            count=fr.read(buf);
        }
        fr.close();
        fw.close();
    }
}

3.带缓冲区的字节输入输出流

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 带缓存区的字节出入输出流
 */
public class TestBufferStream{
    public static void main(String[] args) {

    }
    //
    static void copyFile(String srcPath,String destPath) throws Exception{
        //节点流
        FileInputStream fis=new FileInputStream(srcPath);
        BufferedInputStream bis=new BufferedInputStream(fis);

        FileOutputStream fos=new FileOutputStream(destPath);
        BufferedOutputStream bos=new BufferedOutputStream(fos);

        byte[] buf=new byte[10];
        int count=bis.read(buf);
        while(count!=-1){
            bos.write(buf,0,count);
            count=bis.read(buf);
        }
        //处理流的关闭问题,之关闭处理流即可
        bis.close();
        bos.close();
        //如果节点流和处理流都关闭,那么先打开的后关闭
    }
}


今日练习:

1:接收键盘输入,将输入的内容写入到指定的文件,当输入bye 的时候,结束输入。

(转) :Java 编程下 IO 中的输入流的 read() 方法返回值为什么是 int 值

Java 下 IO 中 FileReder 和 FileInputStream 分别是以字符和字节的形式来完成数据的读取的,然而返回值确是 int 类型的数据,这样做的核心目的只是要取到到一个 int 类型下的 -1 来表示数据流的末尾。为什么要这样做?又是怎么实现的呢?

首先看 FileReder :

FileReader fr = new FileReader(“src.txt”);
int ch = fr.read();

如上面的代码,FileReader 的 read 方法返回值是一个 int 类型的变量来接收的,然而 read 方法在实际中却是以字符形式来进行数据的读取的。通过上面的基本数据类型的取值范围我们能发现 char 类型数据的取值范围为 0 ~ 65535 ,也就是说 char 类型数据是取不到负值的;int 类型数据的取值范围为 -2147483648 ~ 2147483647 ,可以取到负值;同时 int 的取值范围又包含 char 的取值范围,这就为使用 int 作为返回值类型提供了可能,因为流需要一个特殊的值来表示流末尾,这个值不应该在 char 的取值范围内,如果使用 char 取值范围内的值作为流末尾标志,那么这个值同样有可能出现在数据流中间作为数据来传输,流在读到这个值的时候会认为已经到达流末尾,后面未读取的数据将被截断。所以 Java 中选择了使用 -1 来作为流末尾,这个值不在 char 的取值范围内,所以不存在数据截断,然而 -1 又在 int 的取值范围内,同时 int 的取值范围包含 char 的取值范围,所以 FileReader 下 read 方法返回的 char 类型数据直接转为了 int 类型。

再看 FileInputStream :

FileInputStream fis = new FileInputStream(“src.txt”);
int b = fis.read();

同理 FileInputStream 也需要一个自己取不到的值来作为流末尾的标志,Java 同样使用 -1 来作为字节流的流末尾,从上面基本数据类型的取值范围我们可以看到 byte 的取值范围为 -128 ~ 127 ,这就意味走着 byte 可以取到 -1 ,如果把 -1 直接当作 int 作为流末尾,那么就无法区分这个读到的结果是流末尾还是流中的数据了,那么 Java 是如何实现取值 -1 的呢?在 Java 内部,Java 通过高位补 0 来实现数据从 byte 到 int 的转换,举个例子:

-1 在 byte 类型和 int 类型中都可以取到,-1 在 byte 类型下的二进制存储形式为 11111111 ,然而使用 read 方法的时候,Java 内部将 byte 的高位补 0 将 byte 转为 int 类型,所以 byte 类型的 -1 在 int 类型下的二进制存储形式为 00000000 00000000 00000000 11111111,对应的 int 值为 255,通过高位补 0 ,所有 byte 类型的负数都转为了正数。然而在使用这些读到的 byte 数据时,只要将这些数据从 int 强转回 byte 即可得到原有的数据。所以就可以使用 -1 来作为流末尾的标志,因为 Java 内部将 byte 的负数通过高位补 0 将其转换为了负数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值