Java进阶 —— 集合、IO流

前言

        学完Java的面向对象知识,我们需要更加深入地去了解Java中的集合、IO和反射这些进阶的内容,真正从Java的入门状态迈步走向进阶状态哈哈哈。当然了在接下来的几篇文章中荔枝也会介绍有关注解的相关知识,希望大家读完能有所收获哈哈哈~~~


文章目录

前言

一、Java集合

1.1 HashSet

1.2 TreeSet

1.3 List集合

1.4 Map

1.5 操作集合的工具类:Collections

二、Java IO  

2.1 File类

2.2 IO原理和流的分类

2.3 文件流

2.3.1 文件字节流-FileInputStream

2.3.2 文件字符流

2.4 缓冲流

2.4.1 缓冲字节流

2.4.2 缓冲字符流

2.5 转换流

2.6 标准输入输出流

2.7 对象流

2.8 随机存取文件流

总结


一、Java集合

Java集合类存放于iava.util包中,是一个用来存放对象的容器。Java中的集合有如下几个特点:

  • 集合只能存放对象。比如你存一个it型数据1放入集合中,其实它是自动转换成Integer类后存入的,Java中每一种基本类型都有对应的引用类型。
  • 集合存放的是多个对象的引用,对象本身还是放在堆内存中。
  • 集合可以存放不同类型,不限数量的数据类型。

Java集合可分为Set、List和Map:

  • Set:无序、不可重复的集合
  • List:有序,可重复的集合
  • Map:具有映射关系的集合

1.1 HashSet

HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。我们大多数时候说的set集合指的都是HashSet。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet特点:

  • 不能保证元素的排列顺序
  • 不可重复
  • HashSet不是线程安全的
  • 集合元素可以使null

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置。如果两个元素的equals()方法返回true,但它们的HashCode()返回值不相等,HashSet将会把它们存储在不同的位置,但依然可以添加成功。

HashSet集合框架

HashSet类作为一个实现类实现set接口,set接口则继承了Collection接口中的所有方法。collection中主要的方法有:

package test;
import java.util.HashSet;
import java.util.Set;

public class Test{
    public static void main(String[]args){
        Set set = new HashSet();
        //为集合添加元素
        set.add(1);
        set.add("a");
        System.out.println(set);

        //移除集合中的元素
        set.remove(1);
        System.out.printin(set);

        //查看hashset集合中是否存在a
        System.out.println(set.contains("a"));

        //清空集合
        set.clear();
    }
}

遍历集合的方法

Set set = new HashSet(); //等价于Set set = new HashSet(); 
//使用迭代器进行遍历
Iterator it = set.interator();
while(it.hasNext()){
    System.out.println(it.next());
}

//for each迭代集合
for(Object obj:set){
     System.out.println(obj);
} 
  

1.2 TreeSet

        TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。TreeSet类实现了Navigable接口,该接口继承SortedSet接口并继承Set接口和Collection接口。对于TreeSet类中的方法和遍历集合方法和HashSet是一样的。

自然排序

        TreeSet会调用集合元素的compareTo(Object obi)方法来比较元素之间的大小关系,然后将集合元素按升序排列。需要注意的是:必须放入同样类的对象(默认会进行排序)否则可能会发生类型转换异常.我们可以使用泛型来进行限制,可以通过泛型来实现它。

定制排序

定制排序需要设计一个实现类实现Java中的Comparator接口并实现实现类的compare方法体。

class Person implements Comparator{
//把person对象存到Treeset中并且按照年龄排序
    int age;
    String name;
    //无参构造
    public Person(){
        ...
    }
    //有参构造
    public Person(String name,int age){
        this.name = name;
        this.age = age; 
    }
    @Override
    public int compare(Person ol,Person o2){
        if(o1.age > o2.age){
            return 1;  
        }else if(ol.age < o2.age){
            return -1;
        )else{
            return 0;
    }
}
Set set = new TreeSet<>(new Person());

1.3 List集合

        List代表一个元素有序、且可重复的集合,集合中的每一个元素都有其对应的顺序索引。List允许使用重复元素,可以通过索引来访问指定位置的集合元素get()。List默认按元素的添加顺序设置元素的索引,同时List集合里添加了一些根据索引来操作集合元素的方法。ArrayList实现了List接口,而list接口继承了Collection接口。

List list = new ArrayList();
//在指定索引下标之间插入数据
list.add(1,"f");
//在指定下标插入集合
list.addAll(1,list1);
//获取指定元素在集合中的下标
list.indexOf("d");
//获取指定元素在集合中的最后一次出现的下标
list.lastIndexOf("d");
//根据指定的索引下标移除元素
list.remove(2);
//修改
list.set(1,"111");
//截取list集合形成一个新的集合,左闭右开
List sublist = list.subList(2,4);

需要注意的是ArrayList是线程不安全的。 

1.4 Map

        Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的Key,另外一组用于保存Map里的Value,Map中的key和value都可以是任何引用类型的数据,Map中的Key不允许重复,即同一个Map对象的任何两个Key通过equals方法比较中返回false。需要注意的是Key和Value之间存在单向一对一关系,即通过指定的Key总能找到唯一的,确定的Value。

Map接口是通过HashMap类来实现的。

Map map = new HashMap();

接口方法

//map添加元素
map.put("a",1);

//根据key获取值
map.get("a");

//根据key来移除
map.remove("a");

//判断是否存在key
map.containsKey("a");

遍历Map集合

//获取map集合的key的集合
map.keySet()

map.values //获取集合的所有values

//获取keys再用for...each
Set keys = map.keySet()

///通过map,entrySet(),再用for...each;
Set> entrys = map.entryset();
for(Entryen : entrys){
    System.out.println("key:"en.getkey()+",value:"en.getvalue());
)

1.5 操作集合的工具类:Collections

Collections是一个操作Set、List和Map等集合的工具类,Collections中提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
排序操作:

  • reverse(List):反转List中元素的顺序
  • shuffle(List):对List集合元素进行随机排序
  • sort(List):根据元素的自然顺序对指定List集合元素按升序排序
  • sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
  • swap(List,int,int):将指定Iist集合中的i处元素和j处元素进行交换 

二、Java IO  

2.1 File类

        java.io.File类,File能新建、删除、重命名文件和目录,但不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。File对象可以作为参数传递给流的构造函数。

File类的常见构造方法:

import java.io.File;
public class Test{
    public static void main(String[] args)(
        File f = new File("D:\\test\\tt.txt");/这个时候对象f就是tt.txt文件
    
        //File f2 = new File("D:/test/tt.txt");

        //File f3 = new File("D:" + File.separator" + "test\\tt.txt");

        //File f4 = new Fi1e("D:\\test","tt,txt")://这个i也是tt.txt文件,这种使用相对比较少

    //注意,\在文件中是路径的分隔符,但是在Java编程中一个\的意思是转移符,在Java中\\或者/才是文件的分隔符
    //也可以Fi1e.separator作为文件分隔符
    }
}

 注意:以下的方法都是基于构造了一个File对象之后滴~

访问文件名

  • getName():获取当前文件名称或文件夹的名称
  • getPath():获取创建File对象时候写的路径
  • getAbsoluteFile():返回一个用当前文件绝对路径构建的file对象
  • getAbsolutePath():获取绝对路径
  • getParent():获取当前文件夹的父级路径
  • renameTo(File newName):给文件或文件夹重命名
f.renameTo(neww File("D:\\test\\tt1.txt"));

文件检测

  • exists():判断文件和文件夹是否存在
  • canWrite():判断文件是否可写
  • canRead():判断文件是否可读
  • isFile():判断当前的file对象是否是文件
  • isDirectorv():判断该对象是不是一个文件夹

获取常规文件信息

  • LastModify():获取文件的最后修改时间,返回一个毫秒数
  • Length() :返回文件长度,单位是字节数

文件操作

  • createNewFile():创建一个新文件
  • delete():删除某个文件

目录操作 

  • mkdir():创建单层目录,只能一层一层创建执行madir()方法
  • mkdirs():这个方法可以直接用来创建多层目录
  • list():返回当前file对象路径下的目录和文件的名称
  • listFiles() :返回当前文件夹的子集的file对象
File f = new File("D:\\test");
String [] = f.list();
for(String s:f){
    System.out.println(s);
}

递归方式遍历test下的所有文件

public void test(){
    public static void main(String[] args){
        File f = new File("D:\\test");
        new Test().test(f);
    }
    //递归遍历
    public void test(File file){
        if(file.isFile()){
            System.out.println(file.getAbsolutePath() + "是文件");
        }else{
            //System.out.println(file.getAbsolutePath() + "是文件夹");
            File[] fs = file,listFiles();
            if(fs!=null && fs.length>0){
                for(File f:fs){
                    test(f);
                }
            }
        }
    }
}

2.2 IO原理和流的分类

        Java中的IO流主要是用来处理设备之间的数据传输,数据的输入/输出操作是以“流(Stream)”的方式进行。java.io包下提供了各种“流”类和接口,用来获取不同种类的数据并通过标准的方法输入或输出。

流的分类

  • 按操作数据单位不同分为:字节流(8bit),字符流(16bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流

2.3 文件流

数据流的读写都是基于文件的操作。

2.3.1 文件字节流-FileInputStream

文件字节流基本的输入输出操作 

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Testl{
    public static void main(String[] args){
        Test1.testFileInputStream();
    }

    //文件字节输入流
    public static void testFileInputStream(){
        try{
            FileInputStream in = new FileInputStream("D:/test/abc/ttl.txt");
            byte[] b=new byte[10]://设置一个byte数组接收读取的文件的内容
            
            int len = 0; //设置一个读取数据长度
            while((len = in.read(b)) != -1){
                 System.out.println(new String(b,0,len));//这里的三个参数:参数1是缓冲数据的数组,参数2是从数组的那个位置开始转化字符串,参数3是总共转化几个字节
            }
            //in.read(b);
            in.c1ose()://流在使用完后一定要记得关闭
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //文件字节输出流
    public static void testFileOutputStream(){
       
        try{
            FileOutputStream out = new FileOutputStream("D:/test/abc/tt2.txt");
            String str = "dksjdksdksdjklol";
            out.write(str.getBytes()); //把数据写到内存中
            out.flush(); //把内存中的数据刷写到硬盘中
            out.c1ose();//关闭流

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

字节流复制文件 

学习了基本的文件字节流输入输出的操作后,我们还需要了解文件复制的操作。

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Testl{
    public static void main(String[] args){
        Test1.copyFile("D:/test/abc/ttl.txt","D:/test/abc/tt2.txt");
    }

    public static void copyFile(String inPath,String outPath){
        try{
            FileInputStream in = new FileInputStream(inPath);
            FileOutputStream out = new FileOutputStream(outPath);

            byte[] b = new byte[100]://设置一个byte数组接收读取的文件的内容
            
            int len = 0; //设置一个读取数据长度
            while((len = in.read(b)) != -1){
                 out.write(b,0,len); //把数据写到内存中
            }
            out.flush(); //把内存中的数据刷写到硬盘中
            out.close();
            in.c1ose();//关闭流
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

注意:文件字节流非常通用,可以用来操作字符的文档,还可以操作任何的其他类型文件(图片,压缩包等等) 

2.3.2 文件字符流

文件字节流和文件字符流的差别比较小,主要是以下三个方面:

建立流对象不同

FileReader fr = new FileReader(inpath);

FileWriter fw = new FileWriter(outpath);

临时存放数据的数组不同

char[] ch = new char[1024];

2.4 缓冲流

        前面我们学习了有关文件字符流和文件字节流的相关操作,其中FilelnputStream,FileOutputStream,FileReader,FileWriter都是计算机于其自身的磁盘IO发生的操作,而在缓冲流中数据流的读写都是基于内存的操作。 Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,根据数据操作单位可以把缓冲流分为:BufferedInputStream、 BufferedOutputStream、BufferedReader和BufferedWriter。缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法。对缓冲流使用flush()将会使内存中的数据立刻写出。

简单来说,文件流是计算机直接操作磁盘IO,读写速度会受到硬盘的制约;而缓冲流则是先将数据缓冲到内存中,并在内存中去进行IO操作。 

2.4.1 缓冲字节流

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Test{
    public static void main(String[]args){
        try{
            Test.testBufferedInputstream();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

//缓冲字节输入流
//BufferedInputStream
    public static void testBufferedInputStream() throws Exception{
        //文件字节输入流对象
        FileInputStream in = new FileInputStream("D:\\testdemo\\demo\\src\\day13\\tt.txt")
        //把文件字节输入流放到援冲字节输入流对象
        BufferedInputStream br = new BufferedInputStream(in);
        byte[] b = new byte[100]://设置一个byte数组接收读取的文件的内容
            
        int len = 0; //设置一个读取数据长度
        while((len = in.read(b)) != -1){
             System.out.println(new String(b,0,len));
        }
        //关闭流
        br.close();
        in.close();
    }

    //缓冲字节输出流
    //BufferedOutputStream
    public static void testBufferedOutputStream() throws Exception{
        //创建字节输出流对象
        FileOutputStream out = new FileoutputStream("D:\\testdemo\\demo\\src\\day13\\tt1.txt");
        //把字节输出流对象放到缓冲字节输出流中
        BufferedoutputStream bo = new BufferedoutputStream(out);
        String s = "hello world";
        bo.write(s.getBytes());//写到内存中
        bo.flush();//刷到硬盘上
        bo.close();
        out.close();
    }
}

2.4.2 缓冲字符流

同文件流相同,缓冲流中的字符流和字节流之间的关系也很类似,这里将就不再一一赘述了。

//输入流
FileReader r = new FileReader("D:\\testdemo\\demo\\src\\day13\\tt.txt");
BufferedReader br = new BufferedReader(r);

//输出流
Filewriter fw = new Filewriter("D:\\testdemo\\demo\\src\\day13\\tt3.txt");
BufferedWriter bw = new BufferedWriter(fw);

2.5 转换流

前面已经了解字节流和字符流的概念了,转换流的出现是为了提供一个在字节流和字符流之间的转换。

  • 转换输入流 InputStreamReader
  • 转换输出流 OutputStreamReader
//转换字节输入流为字符输入流
public static void testInputStreamReader() throws Exception{
    FileInputStream fs = new FileInputstream("D:\\testdemo\\demo\\src\\day13\\tt5.txt")
    //把字节流转换为字符流
    InputStreamReader in = new InputStreamReader(fs,"GBK");//参数1是字节流,参数2是编码
    char[] c = new char[100];
    int len = 0;
    while((len = in.read(c))!=-1){
        System.out.printin(new String(c,0,len));
    }
    in.close();
    fs.close ()
}

注意:在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致,否则就会出现乱码。

2.6 标准输入输出流

System.in和System.out分别代表了系统标准的输入和输出,其中System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream的子类。

//标准输出流
System.out.println();
//标准输入流
public static void testSystemIn() throws Exception{
    //创建一个接收键盘输入数据的输入流
    InputStreamReader is = new InputStreamReader(System.in);
    //把输入流放到缓冲流里
    BufferedReader br = new BufferedReader(is);
    String str = "";//定义一个临时接收数据的字符串
    while((str = br.readLine())!=null){
        System.out.println(str);
    }
    br.close();
    is.c1ose();
}

数据输出流
用数据输出流写到文件的中的基本数据类型的数据,是乱码的,不能直接辨认出来,需要数据输入流DataInputStream来读取DataOutputStream。

2.7 对象流

        对象流ObjectInputStream和OjbectOutputSteam用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。需要注意不能序列化static和transient修饰的成员变量。

  • 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中。
  • 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象ObjectOutputStream和ObjectInputStream。

序列化与反序列化对针对的是对象的各种属性,不包括类的属性!

        换句话说,在实际开发中我们需要操作对象实现对象的持久化操作或者是对象的网络传输,所以需要借助对象流来操作对象变成流和从流中恢复对象。如果需要让某个对象支持序列化机制,则必须要使其类也是可序列化的,这就必须实现这两个接口中的一个:Serializble和Externalizable。

//创建一个可以序列化与反序列化的对象
public class Person implements Serializable(
    //一个表示序列化版本标识符的静态变量
    //用来表明类的不同版本间的兼容性
    private static final long serialVersionUID = 1L;
        String name;
        int age;
    }
}

//序列化与反序列化
public class Test{
    public static void main(String[] args){
        try{
            Test.testserialize();
        }catch(Exception e){
            e.printStackTrace();    
        }
    }
    //对象的序列化
    public static void testserialize(){
        //定义对象的输出流,把对象的序列化之后的流放到指定的文件中
        ObjectOutputStream out = new ObiectOutputStream(new FileOutputStream("D:/test/01.txt"));
        Person p = new Preson();
        p.name = "张三";
        p.age = 21;
        out.writeObject(p);
        out.flush();
        out.close();
    }

    //对象的反序列化
    public static void testDeserialize() throws Exception{
        /创建对象输入流对象,从指定的文件中把对象序列化后的流读取出
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/test/01.txt");
        Object obj = in.readObject();
        Person p = (Person)obj;
        in.close();
    }
}

2.8 随机存取文件流

        RandomAccessFile类支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读、写文件。支持只访问文件的部分内容、可以向己存在的文件后追加内容。RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile类对象可以自由移动记录指针:

  • long getFilePointer():获取文件记录指针的当前位置
  • void seek(long pos):将文件记录指针定位到pos位置

RandomAccessFile类的构造器

  • public RandomAccessFile(File file,String mode)
  • public RandomAccessFile(String name,String mode)

mode参数设置:

  • r:以只读方式打开
  • rw:打开以便读取和写入
  • wd:打开以便读取和写入;同步文件内容的更新
  • ws:打开以便读取和写入;同步文件内容和元数据的更新

随机读 

public class Test{
    public static void main(String[]args){
        
    }

    public static void testRandomAccessFileRead() throws Exception{
        //RandomAccessFi1e的构造有两个参数,参数1是读写的文件的路径,参数2是指定RandomAccessFile的访问模式
        RandomAccessFile ra = new RandomAccessFile("D:/test/tt10.txt","r");
        ra.seek(0);   //设置读取文件内容的起始点
        byte[] b = new byte[1024];
        int len = 0;
        while((len = ra.read(b))!=-1)(
            System.out.println(new String(b,0,len));
        }
        ra.close();
    }
}

随机写

public static void testRandomAccessFileWrite() throws Exception{
    RandomAccessFile ra = new RandomAccessFile("D:/test/tt10.txt","rw");
    //ra.seek(0);//设置写的起始点,o代表从开头写
    ra.seek(ra.1ength());//设置写的起始点,xa.length()代表从文件的最后结尾写,也就是文件的追加
    ra.write("你好".getBytes());
    ra.close();
}

总结

        在这篇文章中,荔枝主要梳理了Java中有关集合和IO流的知识,包括几种集合类型和操作工具类、以及多种IO流的相应读写操作,荔枝同时也给出了相应的代码示例,希望能帮助到未来滴自己哈哈哈,有需要的小伙伴可以看看哈哈哈~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

你可能感兴趣的:(Java开发,java,Java,IO,Java集合)