Java SE核心II

作者: 海若Hero | 来源:发表于2018-11-23 13:58 被阅读0次

    1.1.1 Java异常处理机制

    异常结构中的父类Throwable类,其下子类Exceptionlei类和Error类。我们在程序中可以捕获的是Exception的子类异常。

    Error系统级别的错误:Java运行时环境出现的错误,我们不可控。

    Exception是程序级别的错误:我们可控。

    1)异常处理语句:try-catch,如果try块捕获到异常,则到catch块中处理,否则跳过忽略catch块(开发中,一定有解决的办法才写,无法解决就向上抛throws)。

     try{//关键字,只能有一个try语句
    
     可能发生异常的代码片段
    
     }catch(Exception e){//列举代码中可能出现的异常类型,可有多个catch语句
    
     当出现了列举的异常类型后,在这里处理,并有针对性的处理
    
     }
    

    2)良好的编程习惯,在异常捕获机制的最后书写catch(Exception e)(父类,顶极异常)捕获未知的错误(或不需要针对处理的错误)。

    3)catch的捕获是由上至下的,所以不要把父类异常写在子类异常的上面,否则子类异常永远没有机会处理!在catch块中可以使用方法获取异常信息:

    ①getMessage()方法:用来得到有关异常事件的信息。

    ②printStackTrace()方法:用来跟踪异常事件发生时执行堆栈的内容。

    4)throw关键字:用于主动抛出一个异常

    当我们的方法出现错误时(不一定是真实异常),这个错误我们不应该去解决,而是通知调用方法去解决时,会将这个错误告知外界,而告知外界的方式就是throw异常(抛出异常)catch语句中也可抛出异常。虽然不解决,但要捕获,然后抛出去。

    使用环境:

    我们常在方法中主动抛出异常,但不是什么情况下我们都应该抛出异常。原则上,自身决定不了的应该抛出。那么方法中什么时候该自己处理异常什么时候抛出?

    方法通常有参数,调用者在调用我们的方法帮助解决问题时,通常会传入参数,若我们方法的逻辑是因为参数的错误而引发的异常,应该抛出,若是我们自身的原因应该自己处理。

     public static void main(String[] args) {
    
     try{/**通常我们调用方法时需要传入参数的话,那么这些方法,JVM都不会自动处理异常,而是将错误抛给我们解决*/
    
     String result=getGirlFirend("女神"); System.out.println("追到女神了么?"+result);
    
     }catch(Exception e){
    
     System.out.println("没追到");//我们应该在这里捕获异常并处理。
    
     }
    
     }
    
     public static String getGirlFirend(String name){
    
     try{ if("春哥".equals(name)){ return "行";
    
     }else if("曾哥".equals(name)){ return "行";
    
     }else if("我女朋友".equals(name)){ return "不行";
    
     }else{/**当出现了错误(不一定是真实异常)可以主动向外界抛出一个异常!*/
    
     throw new RuntimeException("人家不干!");
    
     }
    
     }catch(NullPointerException e){
    
     throw e;//出了错不解决,抛给调用者解决
    
     }
    
     }
    

    5)throws关键字:不希望直接在某个方法中处理异常,而是希望调用者统一处理该异常。声明方法的时候,我们可以同时声明可能抛出的异常种类,通知调用者强制捕获。就是所谓的“丑话说前面”。原则上throws声明的异常,一定要在该方法中抛出。否则没有意义。相反的,若方法中我们主动通过throw抛出一个异常,应该在throws中声明该种类异常,通知外界捕获。

    u 注意事项:

    v 注意throw和throws关键字的区别:抛出异常和声明抛出异常。

    v 不能在main方法上throws,因为调用者JVM直接关闭程序。

     public static void main(String[] args) {
    
     try{ Date today=stringToDate("2013-05-20"); 
    } catch (ParseException e){
    
     //catch中必须含有有效的捕获stringToDate方法throws的异常
    
     // 输出这次错误的栈信息可以直观的查看方法调用过程和出错的根源
    
     e.printStackTrace(); 
    } 
    }
    

    eg:将一个字符串转换为一个Date对象,抛出的异常是字符格式错误java.text.ParseException

     public static Date stringToDate(String str) throws ParseException{
    
     SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-DD");
    
     Date date=format.parse(str);
     return date;  
    }
    

    6)捕获异常两种方式:上例SimpleDataFormat的parse方法在声明的时候就是用了throws,强制我们调用parse方法时必须捕获ParseException,我们的做法有两种:一是添加try-catch捕获该异常,二是在我们的方法中声明出也追加这种异常的抛出(继续往外抛)。

    7)java中抛出异常过程:java虚拟机在运行程序时,一但在某行代码运行时出现了错误,JVM会创建这个错误的实例,并抛出。这时JVM会检查出错代码所在的方法是否有try捕获,若有,则检查catch块是否有可以处理该异常的能力(看能否把异常实例作为参数传进去,看有没有匹配的异常类型)。若没有,则将该异常抛给该方法的调用者(向上抛)。以此类推,直到抛至main方法外仍没有解决(即抛给了JVM处理)。那么JVM会终止该程序。

    8)java中的异常Exception分为:

    ①非检测异常(RuntimeException子类):编译时不检查异常。若方法中抛出该类异常或其子类,那么声明方法时可以不在throws中列举该类抛出的异常。常见的运行时异常有: NullPointerException、 IllegalArgumentException、

    ClassCastException、NumberFormatException、

    ArrayIndexOutOfBoundsException、ArithmeticException

    ②可检测异常(非RuntimeException子类):编译时检查,除了运行时异常之外的异常,都是可检查异常,则必须在声明方法时用throws声明出可能抛出的异常种类!

    9)finally块:finally块定义在catch块的最后(所有catch最后),且只能出现一次(0-1次), 无论程序是否出错都会执行的块! 无条件执行!通常在finally语句中进行资源的消除工作,如关闭打开的文件,删除临时文件等。

     public static void main(String[] args) {
    
     System.out.println(  test(null)+","+test("0")+","+test("") ); }
    
     /**输出结果?1,0,2 ? 4,4,4为正确结果 */
    
     public static int test(String str){
    
     try{ return str.charAt(0)-'0';
    
      }catch(NullPointerException e){ return 1;          
    
      }catch(RuntimeException e){ return 2;          
    
      }catch(Exception e){ return 3;          
    
      }finally{
    //无条件执行 
    return 4; 
    }  
    }
    

    10)重写方法时的异常处理

    如果使用继承时,在父类别的某个地方上宣告了throws某些异常,而在子类别中重新定义该方法时,可以:①不处理异常(重新定义时不设定throws)。②可仅throws父类别中被重新定义的方法上的某些异常(抛出一个或几个)。③可throws被重新定义的方法上的异常之子类别(抛出异常的子类)。

    但不可以:①throws出额外的异常。 ②throws被重新定义的方法上的异常之父类别(抛出了异常的父类)。

    1.1.2 File文件类

    java使用File类(java.io.File)表示操作系统上文件系统中的文件或目录。换句话说,我们可以使用File操作硬盘上的文件或目录进行创建或删除。

    File可以描述文件或目录的名字,大小等信息,但不能对文件的内容操作!File类的构造器都是有参的。

    1)关于路径的描述:不同的文件系统差异较大,Linux和Windows就不同!最好使用相对路径,不要用绝对路径。

    2)“.”代表的路径:当前目录(项目所处的目录),在eclipse_workspace/project_name下,File.separator:常量,目录分隔符,推荐使用!根据系统自动识别用哪种分割符,windows中为/,Linux中为\。

    3)创建该对象并不意味着硬盘上对应路径上就有该文件了,只是在内存中创建了该对象去代表路径指定的文件。当然这个路径对应的文件可能根本不存在!

         File file=new File("."+File.separator+"data.dat");// 效果为./data.dat
    
     File file=new File("e:/XX/XXX.txt");//不建议使用
    

    4)createNewFile()中有throws声明,要求强制捕获异常!

    5)新建文件或目录:

    ①boolean mkdir():只能在已有的目录基础上创建目录。

    ②boolean mkdirs():会创建所有必要的父目录(不存在的自动创建)并创建该目录。

    ③boolean createNewFile():创建一个空的新文件。

    6)创建目录中文件的两种方式:

    ①直接指定data.dat需要创建的位置,并调用createNewFile(),前提是目录都要存在!

    ②先创建一个File实例指定data.dat即将存放的目录,若该目录不存在,则创建所有不存在的目录,再创建一个File实例,代表data.dat文件,创建是基于上一个代表目录的File实例的。使用File(File dir,String fileName)构造方法创建File实例,然后再调用createNewFile():在dir所代表的目录中表示fileName指定的文件

     File dir=new File("."+File.separator+"demo"+File.separator+"A");
    
     if(!dir.exists()){ dir.mkdirs();//不存在则创建所有必须的父目录和当亲目录 }
    
     File file=new File(dir,"data.dat");
    
     if(!file.exists()){file.createNewFile();System.out.println("文件创建完毕!"); }
    

    7)查看文件或目录属性常用方法

    ①long length():返回文件的长度。

    ②long lastModified():返回文件最后一次被修改的时间。

    ③String getName():返回文件或目录名。 ⑧String getPath():返回路径字符串。

    ④boolean exists():是否存在。 ⑨boolean isFile():是否是标准文件。

    ⑤boolean isDirectory():是否是目录。 ⑩boolean canRead():是否可以读取。

    ⑥boolean canWrite():是否可以写入、修改。

    ⑦File[] listFiles():获取当亲目录的子项(文件或目录)

    eg1:File类相关操作

     File dir=new File("."); if(dir.exists()&&dir.isDirectory()){//是否为一个目录
    
     File[] files=dir.listFiles();//获取当前目录的子项(文件或目录)
    
     for(File file:files){//循环子项
    
     if(file.isFile()){//若这个子项是一个文件
    
     System.out.println("文件:"+file.getName());
    
     }else{ System.out.println("目录:"+file.getName()); } } }
    

    eg2:递归遍历出所有子项

     File dir=new File(".");        File[] files=dir.listFiles();  if(files!=null&&files.length>0){//判断子项数组有项
    
     for(File file:files){//遍历该目录下的所有子项
    
     if(file.isDirectory()){//若子项是目录
    
     listDirectory(file);//不到万不得已,不要使用递归,非常消耗资源
    
     }else{System.out.println("文件:"+file);//有路径显示,输出File的toString()
    
     //file.getName()无路径显示,只获取文件名
     }
     } 
    }
    

    8)删除一个文件:boolean delete():①直接写文件名作为路径和"./data.dat"代表相同文件,也可直接写目录名,但要注意第2条。②删除目录时:要确保该目录下没有任何子项后才可以将该目录删除,否则删除失败!

     File dir=new File(".");   File[] files=dir.listFiles();
    
     if(files!=null&&files.length>0){ for(File file:files){ if(file.isDirectory()){
    
     deleteDirectory(file);//递归删除子目录下的所有子项 }else{
    
     if(!file.delete()){ throw new IOException("无法删除文件:"+file);  }
    
     System.out.println("文件:"+file+"已被删除!"); }  }
    

    9)FileFilter:文件过滤器。FileFilter是一个接口,不可实例化,可以规定过滤条件,在获取某个目录时可以通过给定的删选条件来获取满足要求的子项。accept()方法是用来定义过滤条件的参数pathname是将被过滤的目录中的每个子项一次传入进行匹配,若我们认为该子项满足条件则返回true。如下重写accept方法。

     FileFilter filter=new FileFilter(){
    
     public boolean accept(File pathname){
    
     return pathname.getName().endsWith(".java");//保留文件名以.java结尾的
    
     //return pathname.length()>1700;按大小过滤 } };
    
     File dir=new File(".");//创建一个目录
    
     File[] sub=dir.listFiles(filter);//获取过滤器中满足条件的子项,回调模式
    
     for(File file:sub){ System.out.println(file);  }
    

    10)回调模式:我们定义一段逻辑,在调用其他方法时,将该逻辑通过参数传入。这个方法在执行过程中会调用我们传入的逻辑来达成目的。这种现象就是回调模式。最常见的应用环境:按钮监听器,过滤器的应用。

    1.1.3 RandomAccessFile类

    可以方便的读写文件内容,但只能一个字节一个字节(byte)的读写8位。

    1)计算机的硬盘在保存数据时都是byte by byte的,字节埃着字节。

    2)RandomAccessFile打开文件模式:rw:打开文件后可进行读写操作;r:打开文件后只读。

    3)RandomAccessFile是基于指针进行读写操作的,指针在哪里就从哪里读写。

    ①void seek(long pos)方法:从文件开头到设置位置的指针偏移量,在该位置发生下一次读写操作。

    ②getFilePointer()方法:获取指针当前位置,而seek(0)则将指针移动到文件开始的位置。

    ③int skipBytes(int n)方法:尝试跳过输入的n个字节。

    4)RandomAccessFile类的构造器都是有参的。

    ①RandomAccessFile构造方法1:

    RandomAccessFile raf=new RandomAccessFile(file,"rw");

    ②RandomAccessFile构造方法2:

    RandomAccessFile raf=new RandomAccessFile("data.dat","rw");

    直接根据文件路径指定,前提是确保其存在!

    5)读写操作完了,不再写了就关闭:close();

    6)读写操作:

     File file=new File("data.dat");//创建一个File对象用于描述该文件
    
     if(!file.exists()){//不存在则创建该文件
    
     file.createNewFile();//创建该文件,应捕获异常,仅为演示所以抛给main了 }
    
     RandomAccessFile raf=new RandomAccessFile(file,"rw");//创建RandomAccessFile,并将File传入,RandomAccessFile对File表示的文件进行读写操作。
    
     /**1位16进制代表4位2进制;2位16进制代表一个字节 8位2进制;
    
     * 4字节代表32位2进制;write(int) 写一个字节,且是从低8位写*/
    
     int i=0x7fffffff;//写int值最高的8位 raf.write(i>>>24);//00 00 00 7f
    
     raf.write(i>>>16);//00 00 7f ff raf.write(i>>>8);// 00 7f ff ff
    
     raf.write(i);// 7f ff ff ff
    
     byte[] data=new byte[]{0,1,2,3,4,5,6,7,8,9};//定义一个10字节的数组并全部写入文件
    
     raf.write(data);//写到这里,当前文件应该有14个字节了
    
     /**写字节数组的重载方法:write(byte[] data.int offset,int length),从data数组的offset位置开始写,连续写length个字节到文件中 */
    
     raf.write(data, 2, 5);// {2,3,4,5,6}
    
     System.out.println("当前指针的位置:"+raf.getFilePointer());
    
     raf.seek(0);//将指针移动到文件开始的位置
    
     int num=0;//准备读取的int值
    
     int b=raf.read();//读取第一个字节 7f 也从低8位开始
    
     num=num | (b<<24);//01111111 00000000 00000000 00000000
    
     b=raf.read();//读取第二个字节 ff
    
     num=num| (b<<16);//01111111 11111111 00000000 00000000
    
     b=raf.read();//读取第三个字节 ff
    
     num=num| (b<<8);//01111111 11111111 11111111 00000000
    
     b=raf.read();//读取第四个字节 ff
    
     num=num| b;//01111111 11111111 11111111 11111111
    
     System.out.println("int最大值:"+num); raf.close();//写完了不再写了就关了
    

    7)常用方法:

    ①write(int data):写入第一个字节,且是从低8位写。

    ②write(byte[] data):将一组字节写入。

    ③write(byte[] data.int offset,int length):从data数组的offset位置开始写,连续写length个字节到文件中。

    ④writeInt(int):一次写4个字节,写int值。

    ⑤writeLong(long):一次写8个字节,写long值。

    ⑥writeUTF(String):以UTF-8编码将字符串连续写入文件。

    write……

    ①int read():读一个字节,若已经读取到文件末尾,则返回-1。

    ②int read(byte[] buf):尝试读取buf.length个字节。并将读取的字节存入buf数组。返回值为实际读取的字节数。

    ③int readInt():连续读取4字节,返回该int值

    ④long readLong():连续读取8字节,返回该long值

    ⑤String readUTF():以UTF-8编码将字符串连续读出文件,返回该字符串值

    read……

     byte[] buf=new byte[1024];//1k容量 int sum=raf.read(buf);//尝试读取1k的数据
    
     System.out.println("总共读取了:"+sum+"个字节");
    
     System.out.println(Arrays.toString(buf)); raf.close();//写完了不再写了就关了
    

    8)复制操作:读取一个文件,将这个文件中的每一个字节写到另一个文件中就完成了复制功能。

     try { File srcFile=new File("chang.txt");
    
     RandomAccessFile src=new RandomAccessFile(srcFile,"r");//创建一个用于读取文件的RandomAccessFile用于读取被拷贝的文件
    
     File desFile=new File("chang_copy.txt"); desFile.createNewFile();//创建复制文件
    
     RandomAccessFile des=new RandomAccessFile(desFile,"rw");//创建一个用于写入文件的RandomAccessFile用于写入拷贝的文件
    
     //使用字节数组作为缓冲,批量读写进行复制操作比一个字节一个字节读写效率高的多!
    
     byte[] buff=new byte[1024*100];//100k 创建一个字节数组,读取被拷贝文件的所有字节并写道拷贝文件中
    
     int sum=0;//每次读取的字节数
    
     while((sum=src.read(buff))>0){ des.write(buff,0,sum);//注意!读到多少写多少!}
    
     src.close(); des.close(); System.out.println("复制完毕!");
    
     } catch (FileNotFoundException e) { e.printStackTrace();
    
     } catch (IOException e) { e.printStackTrace(); }
    
     //int data=0;//用于保存每一个读取的字节
    
     //读取一个字节,只要不是-1(文件末尾),就进行复制工作
    
     //while((data=src.read())!=-1){ des.write(data);//将读取的字符写入 }
    

    9)基本类型序列化:将基本类型数据转换为字节数组的过程。writeInt(111):将int值111转换为字节并写入磁盘;持久化:将数据写入磁盘的过程。

    1.1.4 基本流:FIS和FOS

    Java I/O 输入/输出

    流:根据方向分为:输入流和输出流。方向的定了是基于我们的程序的。流向我们程序的流叫做:输入流;从程序向外流的叫做:输出流

    我们可以把流想象为管道,管道里流动的水,而java中的流,流动的是字节。

    1)输入流是用于获取(读取)数据的,输出流是用于向外输出(写出)数据的。

    InputStream:该接口定义了输入流的特征

    OutputStream:该接口定义了输出流的特征

    2)流根据源头分为:

    基本流(节点流):从特定的地方读写的流类,如磁盘或一块内存区域。即有来源。

    处理流(高级流、过滤流):没有数据来源,不能独立存在,它的存在是用于处理基本流的。是使用一个已经存在的输入流或输出流连接创建的。

    3)流根据处理的数据单位不同划分为:

    字节流:以一个“字节”为单位,以Stream结尾

    字符流:以一个“字符”为单位,以Reader/Writer结尾

    4)close()方法:流用完一定要关闭!流关闭后,不能再通过其读、写数据

    5)用于读写文件的字节流FIS/FOS(基本流)

    ①FileInputStream:文件字节输入流。 ②FileOutputStream:文件字节输出流。

    6)FileInputStream 常用<u>构造方法</u>:

    ①FileInputStream(File file):通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定。即向file文件中写入数据。

    ②FileInputStream(String filePath):通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的文件路径名指定。也可直接写当前项目下文件名。

    常用<u>方法</u>:

    ①int read(int d):读取int值的低8位。

    ②int read(byte[] b):将b数组中所有字节读出,返回读取的字节个数。

    ③int read(byte[] b,int offset,int length):将b数组中offset位置开始读出length个字节。

    ④available()方法:返回当前字节输入流 可读取的总字节数。

    7)FileOutputStream常用<u>构造方法</u>:

    ①FileOutputStream(File File):创建一个向指定File对象表示的文件中写入数据的文件输出流。会重写以前的内容,向file文件中写入数据时,<u>若该文件不存在,则会自动创建该文件。</u>

    ②FileOubputStream(File file,boolean append):append为true则对当前文件末尾进行写操作(追加,但不重写以前的)。

    ③FileOubputStream(String filePath):创建一个向具有指定名称的文件中写入数据的文件输出流。前提路径存在,写当前目录下的文件名或者全路径。

    ④FileOubputStream(String filePath,boolean append):append为true则对当前文件末尾进行写操作(追加,但不重写以前的)。

    常用<u>方法</u>:

    ①void write(int d):写入int值的低8位。

    ②void write(byte[] d):将d数组中所有字节写入。

    ③void write(byte[] d,int offset,int length):将d数组中offset位置开始写入length个字节。

    1.1.5 缓冲字节高级流:BIS和BOS

    对传入的流进行处理加工,可以嵌套使用。

    1)BufferedInputStream:缓冲字节输入流

    A.构造方法:BufferedInputStream(InputStream in)

    BufferedInputStream(InputStream in, int size)

    B.常用方法:

    ①int read():从输入流中读取一个字节。

    ②int read(byte[] b,int offset,int length):从此字节输入流中给定偏移量offset处开始将各字节读取到指定的 byte 数组中。

    2)BufferedOutputStream:缓冲字节输出流

    A.构造方法:BufferedOutputStream(OutputStream out)

    BufferedOutputStream(OutputStream out, int size)

    B.常用方法:

    ①void write(int d):将指定的字节写入此缓冲的输出流。

    ②void write(byte[] d,int offset,int length):将指定 byte数组中从偏移量 offset开始的 length个字节写入此缓冲的输出流。

    ③void flush():将缓冲区中的数据一次性写出,“清空”缓冲区。

    C.内部维护着一个缓冲区,每次都尽可能的读取更多的字节放入到缓冲区,再将缓冲区中的内容部分或全部返回给用户,因此可以提高读写效率。

    3)辨别高级流的简单方法:看构造方法,若构造方法要求传入另一个流,那么这个流就是高级流。所以高级流是没有空参数的构造器的,都需要传入一个流。

    4)有缓冲效果的流,一般为写入操作的流,在数据都写完后一定要flush,flush的作用是将缓冲区中未写出的数据一次性写出:bos.flush();即不论缓存区有多少数据,先写过去,缓冲区再下班~确保所有字符都写出

    5)使用JDK的话,通常情况下,我们只需要关闭最外层的流。第三方流可能需要一层一层关。


    tobehero666.png

    相关文章

      网友评论

        本文标题:Java SE核心II

        本文链接:https://www.haomeiwen.com/subject/xprjqqtx.html