寒假宅喵java进阶upup篇

小fleeting开始了第二阶段的学习啦~~
后面的啃起来有点爬坡的赶脚了呢!

2.1

异常

  • 基本概念
  • Throwable是类,
    Exception和Error都继承了该类,
    其他具体类型继承了Exception和Error
  • 具体类型如下:
    FileNotFoundException文件不存在异常
    ParseException 解析异常,日期字符串转换为日期对象的时候,有可能抛出的异常
    OutOfIndexException 数组下标越界异常
    OutOfMemoryError 内存不足
    ClassCastException 类型转换异常
    ArithmeticException 除数为零
    NullPointerException 空指针异常
    在这里插入图片描述
  • 处理

//常见手段:try catch finally

    //1.将可能抛出FileNotFoundException 文件不存在异常的代码放在try里
    //2.如果文件存在,就会顺序往下执行,并且不执行catch块中的代码
    //3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中
        try{
             。。。。
        }
        catch(。。。。Exception e){//“。。。。Exception”也可以改为其父类,即Exception和Throwable
             。。。。
            e.printStackTrace();     //会打印出方法的调用痕迹
        }
        //无论是否出现异常,finally中的代码都会被执行
        finally{             
             。。。。
        }

//多异常捕捉办法
解决办法之一是分别进行catch
另一个种办法是把多个异常,放在一个catch里统一捕捉
(不过需要通过instanceof 进行判断具体的异常类型)

package exception;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class TestException {
 
    public static void main(String[] args) {
 
        File f = new File("d:/LOL.exe");
 
        try {
            System.out.println("试图打开 d:/LOL.exe");
            new FileInputStream(f);
            System.out.println("成功打开");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date d = sdf.parse("2016-06-03");
        } catch (FileNotFoundException | ParseException e) {
            if (e instanceof FileNotFoundException)
                System.out.println("d:/LOL.exe不存在");
            if (e instanceof ParseException)
                System.out.println("日期格式解析错误");
 
            e.printStackTrace();
        }
 
    }
}
  • throws
    考虑如下情况:
    主方法调用method1
    method1调用method2
    method2中打开文件
package exception;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
 
public class TestException {
 
    public static void main(String[] args) {
        method1();
 
    }
 
    //那么method1就会接到该异常,通过try-catch来处理
    private static void method1() {
        try {
            method2();    //try内是可能发生异常的method2
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
 
    }
    
   
   //method2需要进行异常处理,但它把这个异常通过throws抛出去
    private static void method2() throws FileNotFoundException {
 
        File f = new File("d:/LOL.exe");
 
        System.out.println("试图打开 d:/LOL.exe");
        new FileInputStream(f);
        System.out.println("成功打开");
 
    }
}

throwsthrow这两个关键字接近,不过意义不一样,有如下区别:

  1. throws 出现在方法声明上,而throw通常都出现在方法体内。
  2. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某个异常对象。
  • 自定义异常

创建自定义异常

抛出自定义异常

练习:

package character;

public class Hero{
	public String name;
	protected float hp;
	
	public void attackHero(Hero h)throws EnemyHeroIsDeadException{     //attackHero中进行具体的正常操作,并throw出异常
		if(h.hp==0)
			throw new EnemyHeroIsDeadException(h.name+"挂了,无法攻击");  //实例化自定义异常EnemyHeroIsDeadException,并将其抛出
	}
    
    
	class EnemyHeroIsDeadException extends Exception{  //创建一个自定义异常类EnemyHeroIsDeadException(继承Exception)
		public EnemyHeroIsDeadException(){   //提供一无参的构造方法
			
		}
		public EnemyHeroIsDeadException(String msg){  //提供一 带参的构造方法,并调用父类的对应的构造方法(msg:指代现场信息)
			super(msg);
		}
	}
	
	public void method1(Hero h){      //method1进行异常信息处理
		try{                                   //try内是可能发生异常的attackHero
			this.attackHero(h);
		}catch(EnemyHeroIsDeadException e){
			System.out.println("异常的具体原因:");    //异常处理
			System.out.println(e.getMessage());       //e.Message(),获得现场信息
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args){
		Hero garen=new Hero();
		garen.name="盖伦";
		garen.hp=616;
		
		Hero bingzhang=new Hero();
		bingzhang.name="兵长";
		bingzhang.hp=0;
		
		garen.method1(bingzhang);     //main函数调用method1
	}
}

在这里插入图片描述

练习:
关于自定义异常:类与接口;多个自定义异常;难;
对MyStringBuffer的插入和删除方法中的边界条件判断,用抛出异常来解决
例: insert(int pos, String b) , 当pos 是负数的时候,抛出自定义异常
需要实现自定义两种异常
IndexIsNagetiveException 下标为负异常
IndexIsOutofRangeException 下标超出范围异常
练习:示例3


练习-异常综合

这是一个类图
Account类: 银行账号
属性: balance 余额
方法: getBalance() 获取余额
方法: deposit() 存钱
方法: withdraw() 取钱
OverdraftException: 透支异常,继承Exception
属性: deficit 透支额

在这里插入图片描述

                               代码如下

Accoount.java:

package exception;
import exception.OverdraftException;

public class Account{
    
	protected double balance;
	public Account(double init){
		this.balance=init;
	}
	public double getBalance(){
		
		return balance;
	}
	public void deposit(double amt){
		this.balance+=amt;
	}
	public void withdraw(double amt)throws OverdraftException{
		if(this.balance-amt<0)
			throw new OverdraftException("透支了",amt-this.balance);
		
		this.balance-=amt;
	}
	
	
	public static void main(String[] args) {
	  double init=2000;
	  double amt=3000;
	  
	  Account a=new Account(init);
      //存钱500
      a.deposit(500);
      //查看余额
      System.out.println(a.getBalance());
      
      try{
    	  a.withdraw(amt);
      }catch(OverdraftException e){
    	  System.out.println("透支啦"+e.getDeficit());
    	  e.getStackTrace();
      }
	   
   }
}

OverdraftException.java:

package exception;

public class OverdraftException extends Exception{
   private double deficit;

   public OverdraftException(String msg,double deficit){
	   super(msg);
	   this.deficit=deficit;
   }
   public double getDeficit(){
	   return deficit;
   }
   
}

效果:在这里插入图片描述

============================================================================================================================================

2.2

一、 I/O

  • 文件对象

    • 创建文件对象
package file;
import java.io.File;

public class TestFile {
    
	public static void main(String[] args) {
		
		 // 使用绝对路径或者相对路径创建File对象
		 // 相对路径,相对于工作目录,如果在eclipse中,就是项目目录。以下为绝对路径的例子:
		 File f1=new File("d:/LOLFolder");
		 System.out.println("f1的绝对路径:"+f1.getAbsolutePath());
	     File f2 = new File("LOL.exe");
	     System.out.println("f2的绝对路径:" + f2.getAbsolutePath());
	        
	     // 把f1作为父目录创建文件对象
	     File f3 = new File(f1, "LOL.exe");
	     System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
	     System.out.println("判断是否存在:"+f3.exists());   
	     //可以得出即使实际文件不存在也创建出路径的
	    
	}
}
//另外,通过 System.out.println(f1) 也不会出现编译错误,
//只不过f1.getAbsolutePath()得到的是String类型。
//eg:System.out.printf("最小的文件是%s",f.getAbsoluteFile());
 - 文件常用办法
package file;
import java.io.File;
import java.util.Date;


public class TestFile {
    
	public static void main(String[] args) {
		 File f=new File("c:/project/j2se/src/character/Hero.java");
		 System.out.println("当前文件是:\t"+f);
		 
		 System.out.println("判断文件是否存在:\t"+f.exists());
		 System.out.println("判断是否是文件:\t"+f.isFile());
		 System.out.println("判断是否是文件夹:\t"+f.isDirectory());
	     System.out.println("获取文件长度:\t"+f.length());
//注意!f.length()返回的是long类型。因为long类型很多运算无法进行,所以有的时候需要把它强制转换成int类
	     
	     long time=f.lastModified();
	     System.out.println("获取文件的最后修改时间数值:\t"+time);
	     Date d= new Date(time);
	     System.out.println("获取文件的最后修改时间日期:\t"+d);
	     

	     /*
	     //设置文件修改时间为1970.1.1 08:00:00
	     f.setLastModified(0);
	     
	     //文件重命名(把Hero.java改成Hero.java)
	     File f2=new File("c:/project/j2se/src/character/Hero2.java");
	     f.renameTo(f2);
	     
	     */
	     
	}
}

在这里插入图片描述

package file;
import java.io.File;
import java.io.IOException;

public class TestFile {
    
	public static void main(String[] args) {
		
		File f=new File("c:/project/j2se/src/character");
		//f.list()以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹):
		System.out.println("当前文件夹下的所有文件");
		String ff[]=f.list();
		//输出看看
		for(String i:ff)
			System.out.printf("%s   ",i);
		System.out.println();
		// f.listFiles()以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
		File[]fs= f.listFiles();
		
		
		
		//f.getParent()以字符串形式返回获取所在文件夹
		String f1=f.getParent();
		System.out.println("该文件所在文件夹"+f1);

		//f.getParentFile()以文件形式返回获取所在文件夹
		File f2=f.getParentFile();
		System.out.println("该文件所在文件夹"+f2);


		// 创建文件夹
		//如果父文件夹不存在,则用mkdirs创建一个父文件夹;若存在则mkdir。用反会报错。
		//此处父文件夹src存在,故用mkdir:
		f.mkdir();

		
		/* f.createNewFile();
		 * 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
		  所以创建一个空文件之前,通常都会创建父目录
        f.getParentFile().mkdirs();*/
  
        // 列出所有的盘符c: d: e: 等等
          f.listRoots();

  
        // 刪除文件
        f.delete();
  
        // JVM结束的时候,刪除文件,常用于临时文件的删除
        f.deleteOnExit();
	    
	}
}

在这里插入图片描述

  • **练习-遍历文件夹 **

一般说来操作系统都会安装在C盘,所以会有一个 C:\WINDOWS目录。
遍历这个目录下所有的文件(不用遍历子目录)
找出这些文件里,最大的和最小(非0)的那个文件,打印出他们的文件名
注: 最小的文件不能是0长度

package file;
   
import java.io.File;
   
public class TestFile {
   
    public static void main(String[] args) {
        File f = new File("c:\\windows");
        File[] fs = f.listFiles();
        if(null==fs)
            return;
        long minSize = Integer.MAX_VALUE;
        long maxSize = 0;
        File minFile = null;
        File maxFile = null;
        for (File file : fs) {
            if(file.isDirectory())
                continue;
            if(file.length()>maxSize){
                maxSize = file.length();
                maxFile = file;
            }
            if(file.length()!=0 && file.length()<minSize){
                minSize = file.length();
                minFile = file;
            }
        }
        System.out.printf("最大的文件是%s,其大小是%,d字节%n",maxFile.getAbsoluteFile(),maxFile.length());
        System.out.printf("最小的文件是%s,其大小是%,d字节%n",minFile.getAbsoluteFile(),minFile.length());
   
    }
}

在这里插入图片描述
改:遍历子文件夹

//使用递归来遍历一个文件夹的子文件
    public static void listFiles(File file){
        if(file.isFile()){
            if(file.length()>maxSize){
                maxSize = file.length();
                maxFile = file;
            }
            if(file.length()!=0 && file.length()<minSize){
                minSize = file.length();
                minFile = file;
            }
            return;
        }
          
        if(file.isDirectory()){
            File[] fs = file.listFiles();
            if(null!=fs)
            for (File f : fs) {
                listFiles(f);
            }
        }
    }
    public static void main(String[] args) {
        File f = new File("c:\\windows");
        listFiles(f);
    }


  • 在这里插入图片描述

输入流: InputStream (比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流)
输出流:OutputStream
-用于以字节的形式读取和写入数据
-数据源可以是文件,还可以是数据库,网络甚至是其他的程序

示例:

之前:在这里插入图片描述
代码:

package file;
   
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
   
public class TestFile {
   
    public static void main(String[] args) {
        
    	//**输入流
    	try{
    	创建:
    		
    		//创建文件。
    	    File f = new File("d:/lol.txt");
            // 创建基于文件的输入流。通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
            FileInputStream fis = new FileInputStream(f);
            //创建字节数组,其长度就是文件的长度
            byte[] b =new byte[(int)f.length()];
            
        读取:
            //以字节流的形式读取文件所有内容
            fis.read(b);
            for(byte i:b)
            {
            	System.out.println(i);
            }
            //每次使用完流,都应该进行关闭
            fis.close();
            
    	}catch(IOException e){
    		e.printStackTrace();
    	}
    	
    	
    	//**输出流
        try {
        	      	
       准备:
            // 准备文件lol2.txt(其中的内容是空的)
            File f = new File("d:/lol2.txt");
            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(f);
            // 准备字节数组
            byte data[] = { 88, 89 };
 
        写出:    
            // 把数据写到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
             
        } catch (IOException e) {
            e.printStackTrace();
        }
    	
    	//注: 如果文件d:/lol2.txt不存在,写出操作会自动创建该文件。
    	//但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常
    }
}

之后:
在这里插入图片描述
在这里插入图片描述
解析;A->65,B->66;88->X,89->Y.

  • 改进:

    以字节流的形式向文件写入数据,自动创建中间不存在的目录
 try {
            File f = new File("d:/xyz/abc/def/lol2.txt");
             
            //因为默认情况下,文件系统中不存在 d:\xyz\abc\def,所以输出会失败
             
            //首先获取文件所在的目录
            File dir = f.getParentFile();
            //如果该目录不存在,则创建该目录
            if(!dir.exists()){
                dir.mkdirs(); //使用mkdirs则会把不存在的目录都创建好
            }
 
            byte data[] = { 88, 89 };
 
            FileOutputStream fos = new FileOutputStream(f);
 
            fos.write(data);
 
            fos.close();
 
        } catch (IOException e) {
            e.printStackTrace();
        }

则,会自动将中间的目录不存在的目录\xyz\abc\def都实体化创建好
在这里插入图片描述

  • 练习:拆分文件

找到一个大于100k的文件,按照100k为单位,拆分成多个子文件,并且以编号作为文件名结束。
比如文件 eclipse.exe,大小是309k。
拆分之后,成为
eclipse.exe-0
eclipse.exe-1
eclipse.exe-2
eclipse.exe-3

拆分的思路,先把源文件的所有内容读取到内存中,然后从内存中挨个分到子文件里
提示,这里用到了数组复制Arrays.copyOfRange

package file;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;


public class TestFile {
 
    public static void main(String[] args) {
        
    	     int eachsize=100*1024; //100k  

             //创建源文件及其相关量   
    	     File f = new File("d:/2.doc");
    	     byte fileContent[]=new byte[(int)l];
             long l=f.length();
             System.out.println("源文件大小:"+l);
             
             //先读取
              通过输入流,将文件f的数据读进来
             try {         
                 FileInputStream fis=new FileInputStream(f);
                 fis.read(fileContent);
                 fis.close();
                 
             } catch (IOException e){
                 e.printStackTrace();
             }
             
             
             分批写出
             int i=0;
             int n=(int)l/eachsize+(int)((l%eachsize>0)?1:0);   计算需要被划分成多少份子文件
             for(i=0;i<n;i++)
             {
                 //准备:每一份子文件eachFile及其相关量
                 File eachFile=new File(f.getParent(),eachFileName);
            	 String eachFileName=f.getName()+"-"+i;
            	 byte eachContent[]=new byte[eachsize]; 
                  // 通过数组复制Arrays.copyOfRange。将源文件的内容复制部分数据到eachFile
                    if(i!=n-1) // 除开最后一个文件,其他文件大小都是100k
                	    eachContent=Arrays.copyOfRange(fileContent,eachsize*i, eachsize*(i+1)-1);
                     else// 最后一个文件的大小是剩余的
                	    eachContent=Arrays.copyOfRange(fileContent,eachsize*i, (int)l-1);
                 
                 //写出:通过输出流,把eachfile里的数据写出去
                 try{
                	 FileOutputStream fos=new FileOutputStream(eachFile);
                	 fos.write(eachContent);
                	 fos.close();
                	 System.out.printf("输出子文件%s,其大小为%,d字节 %n",eachFileName,eachFile.length());	 
                   }catch (IOException e) {
                     e.printStackTrace();
                 }
                       
          }

    }
}

输出结果:
在这里插入图片描述
在这里插入图片描述
💗文件及其相关量:
文件类型(File)
内容 (字节数组byte[ ]);
名字(String);
长度(long) …

💗字节数组像是输入输出的纽带:流入的数据在那存着,流出的数据从它那去。
像是一个保存的容器:它存放保留着数据
是一个流的媒介:输入输出以字节的形式进行。

  • 练习:拆分文件

不需要把所有的子文件都先读取到内存中,而是一边读取子文件的内容,一边写出到目标文件

package file;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Arrays;


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

             try {
            	 
            	   //准备最后目标文件及其相关量
            	   File f = new File("d:","合成2.doc");
            	   FileOutputStream fos=new FileOutputStream(f);
            	 
            	   //边读边出
            	   int  n=0; //子文件的数目
            	   while(true)
            	   {
            		   判断是否进行本轮循环
            		   File eachfile = new File("d:","2.doc"+"-"+n++);
            		   if(!eachfile.exists()) 直到没有文件可以读
            		     	 break;
            		 
            		   读取子文件的内容
            		   byte[] eachcontent=new byte[(int)eachfile.length()];
            		   FileInputStream fis=new FileInputStream(eachfile);
                       fis.read(eachcontent);
                       fis.close();
                     
                       把子文件的内容写出去
                       fos.write(eachcontent);
                       fos.flush();  //保证缓存清空输出
                       System.out.printf("把子文件 %s写出到目标文件中%n",eachfile);
            		 
            	   }
            	   fos.close();
                   System.out.printf("最后目标文件的大小:%,d字节" , f.length());
                
                 }catch (FileNotFoundException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
    }
}

在这里插入图片描述
在这里插入图片描述

  • 关闭流
  1. 在try中关闭,存在巨大的资源占用隐患。
  2. 在finally中关闭
FileOutputStream fos = null;  // 为了在finally中关闭,声明在try外面
try{
	fos= new FileOutputStream(f);
	。。。。。。
   }catch(IOException e){
	  e.printStackTrace();
}finally{
	try{
		if(fos!=null)
			fos.close();
	   }catch(IOException e){
			e.printStackTrace();
	    }
}
  1. 在try()中关闭

        try (FileInputStream fis = new FileInputStream(f)) {
        //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
            。。。。。。
        } catch (IOException e) {
            e.printStackTrace();
        }

============================================================================================================================================

2.3

在这里插入图片描述

一、 I/O流(续上)

  • 字符流
  • Reader字符输入流:
    Writer字符输出流
    专门用于字符的形式读取和写入数据
  • 相较字节流。除三处改动外其余不变
    1.字符数组的类型从byte变为char
    2.流的创建变成了try()的形式(故无需关闭流)
    在括号里边进行FileReader fr = new FileReader(f) 和 FileWriter fr = new FileWriter(f),
    3.import java.io.FileReader; import java.io.FileWriter;

练习:文件加密

加密算法:
数字:如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4
如果是9的数字,变成0
字母字符:如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z->a, Z-A。字符需要保留大小写
非字母字符:比如’,&^ 保留不变,中文也保留不变

package file;
 
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Arrays;


public class TestFile {
 
	public static void encodeFile(File encodingFile, File encodedFile){
		
		char acontent[]=new char [(int)encodingFile.length()];
		
		//先读如成为字符流
		try(FileReader fr= new FileReader(encodingFile)){
			
			fr.read(acontent);
			//处理
			for(int i=0;i<(int)encodingFile.length();i++){
				acontent[i]=encodeChar(acontent[i]);
			}
		}catch(IOException e){
			e.getStackTrace();
		}
		
		
		//写出
		try(FileWriter fw= new FileWriter(encodedFile)){
			
			fw.write(acontent);
			
		}catch(IOException e){
			e.getStackTrace();
		}	
		
	}
	
	public static char encodeChar(char c){
		if(c>'0'&&c<='9'||c>'a'&&c<='z'||c>'A'&&c<='Z')
			c--;
		if(c=='0')
			c='9';
		if(c=='a')
			c='z';
		if(c=='A')
			c='Z';
		
		return c;
		/*方二:          
		if (isLetterOrDigit(c)) {
        switch (c) {
        case '9':
            c = '0';
            break;
        case 'z':
            c = 'a';
            break;
        case 'Z':
            c = 'A';
            break;
        default:
            c++;
            break;
        
         }*/
	}
	
	
    public static void main(String[] args) {
      //创建文本
       File f=new File("d:/lol.txt");
   
      //准备文件
       File f2=new File("d:/","lol2.txt");
       
      //调用
       encodeFile(f,f2); 
   }
}

  • 中文问题

    • 不同的编码方式对应不同的棋盘

    ISO-8859-1 ASCII 数字和西欧字母
    GBK GB2312 BIG5 中文
    UNICODE (统一码,万国码)
    并派生了各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节

    • 以字符 中 为例,查看其在不同编码方式下的值是多少
      也即在不同的棋盘上的位置
      在这里插入图片描述
      在这里插入图片描述
    • 文件的编码方式

    用记事本打开任意文本文件,并且另存为,就能够在编码这里看到一个下拉。
    eclipse也有类似的编码方式,右键任意文本文件,点击最下面的"property"
    就可以看到Text file encoding
    在这里插入图片描述

    • 用FileInputStream 字节流正确读取中文
      -先把它读成字节流,再放到对应编码的棋盘上。这样会识别出对应字符
    • 识别出对应的中文:new String(字节数组,“要放的编译棋盘”)在这里插入图片描述
    • 用FileReader 字符流正确读取中文

FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:(在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。)

new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 

示例:

package Stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
 
public class TestStream {
 
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File f = new File("d:\\lol.txt");
        System.out.println("默认编码方式:"+Charset.defaultCharset());
        //FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
        //而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
        try (FileReader fr = new FileReader(f)) {
            char[] cs = new char[(int) f.length()];
            fr.read(cs);
            System.out.printf("FileReader会使用默认的编码方式%s,识别出来的字符是:%n",Charset.defaultCharset());
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
        //并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {//指定编码方式
            char[] cs = new char[(int) f.length()];
            isr.read(cs);
            System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
}

在这里插入图片描述

解释:什么中字前面有一个?
如果是使用记事本另存为UTF-8的格式,那么在第一个字节有一个标示符,叫做BOM用来标志这个文件是用UTF-8来编码的。

练习:移除BOM

如果用记事本根据UTF-8编码保存汉字就会在最前面生成一段标示符,这个标示符用于表示该文件是使用UTF-8编码的。
找出这段标示符对应的十六进制,并且开发一个方法,自动去除这段标示符。

//前情提要
C:\project\j2se\src\test\2.txt目录下存储的是记事本另存为UTF-8格式的txt文件,内容为“中”。

package Stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

public class TestStream{
	
	public static void main(String[] args) {
		File f = new File("C:\\project\\j2se\\src\\test\\2.txt");
	    
		try(FileInputStream fis=new FileInputStream(f)){
	        
			//读入
			byte[]b=new byte[(int)f.length()];
	        fis.read(b);
	        //首先确认按照UTF-8识别出来有?
	        String str=new String(b,"UTF-8");
	        System.out.println("init:"+str);
	        
	        
	        //打印出文件里所有的数据的16进制
	        for(byte bb:b){
	        	int i=bb&0xff;
	        	System.out.print(Integer.toHexString(i)+" ");
	        }
	        System.out.println();
	        
	        //根据前面的所学,知道'中'字对应的UTF-8编码是:e4 b8 ad
	    	//通过观察法得出 UTF-8的 BOM 是 ef bb bf
	        
	        //去掉BOM
	        byte[]bom=new byte[3];
	        bom[0]=(byte)0xef;
	        bom[1]=(byte)0xbb;
	        bom[2]=(byte)0xbf;
	        byte[]withoutbom=Arrays.copyOfRange(b, bom.length,b.length);
	    	
	        //打印出去掉了BOM之后的数据的16进制
	        for(byte ww:withoutbom){
	        	int i=ww&0xff;
	        	System.out.print(Integer.toHexString(i)+" ");
	        }
	        System.out.println();
	        
	        
	        //将数据识别成中文,并输出
	        String str2=new String(withoutbom,"UTF-8");
	        System.out.println("final:"+str2);
	        
	    } catch (IOException e) {
            // TODO Auto-generated catch block
            System.out.println("出错了");
	    	e.printStackTrace();
        }
	
	}
	
	
}

在这里插入图片描述

  • 缓存流

    • 缓存字符输入流BufferedReader

    可以一次读取一行数据

// 缓存流必须建立在一个存在的流的基础上
		//故先创建文件字符流
		try(FileReader fr =new FileReader(f); BufferedReader br=new BufferedReader(fr)){
			while(true){
		        //一行一行读入
				String str=br.readLine();
				//直至没有
				if(str==null)
					break;
			    System.out.println(str);			
			}
		} catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  • 缓存字符输出流PrintWriter

可以一次写出一行数据

		// 缓存流必须建立在一个存在的流的基础上
		// 故先创建文件字符流
		try(FileWriter fw =new FileWriter(f); PrintWriter pw =new PrintWriter(pw)){
		    // 向文件中输出(写入)三行语句
			pw.println("兵长!");
			pw.println("下一集!");
			pw.println("出现啦!");
			//通过pw.write输出也可以
			}
		} catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	
  • flush
> 需要立即把输出(数据写入到硬盘),而不是等缓存满了才写出去。 
            pw.println("兵长!");
            //强制把缓存中的数据写入硬盘,无论缓存是否已满
            pw.flush();   

练习:移除注释
设计一个方法,用于移除Java文件中的注释
public void removeComments(File javaFile)
比如,移出以//开头的注释行

public static void removeComments(File javaFile) {
        StringBuffer sb = new StringBuffer();
        //读取内容
        try (FileReader fr = new FileReader(javaFile); BufferedReader br = new BufferedReader(fr);) {
            while (true) {
                String line = br.readLine();
                if (null == line)
                    break;
                //如果不是以//开头,就保存在StringBuffer中
                if (!line.trim().startsWith("//"))//trim去头尾多余空格
                    sb.append(line).append("\r\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        try (
            FileWriter fw = new FileWriter(javaFile);
            PrintWriter pw = new PrintWriter(fw);
        ) {
            //写出内容
            pw.write(sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
  • 数据流

    文件与数据 (int类,boolean类,String类)
  • DataInputStream 数据输入流
    DataOutputStream 数据输出流
        try (
                FileInputStream fis  = new FileInputStream(f);
                DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
            。。。
          }。。。
        try (
                FileOutputStream fos  = new FileOutputStream(f);
                DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(300);
            dos.writeUTF("123 this is gareen");
              。。。
          }。。。
  • 对象流

    文件与对象Object
    注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口
    下例:对象是Hero

则TestStream.java

 try(
            //创建对象输出流
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos =new ObjectOutputStream(fos);
            //创建对象输入流              
            FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois =new ObjectInputStream(fis);
        ) {
            oos.writeObject(h);
            Hero h2 = (Hero) ois.readObject();
               
          。。。
      }。。。

且Hero.java

package charactor;
 
import java.io.Serializable;
 
public class Hero implements Serializable {
    //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;
 
}

ADD:
准备一个长度是10,类型是Hero的数组, 使用10个Hero对象初始化该数组

//创建Hero数组
Hero h[] =new Hero[10];
for (int i = 0; i < h.length; i++) {
h[i] = new Hero(“hero:” +i);
}

另 ,Hero.java中需要构造
 public Hero(String str){
	this.name=str;
}
  • 控制台流

    System.out 是常用的在控制台输出数据的
    System.in 可以从控制台输入数据

//System.in 输入

import java.io.InputStream;
try (InputStream is = System.in;) {
            while (true) {
                // 敲入a,然后敲回车可以看到
                // 97 13 10
                // 97是a的ASCII码
                // 13 10分别对应回车换行
                int i = is.read();
                System.out.println(i);
            }
        } catch...

//Scanner输入

import java.util.Scanner;

main:
Scanner s = new Scanner(System.in);
             
            while(true){
                String line = s.nextLine();
                System.out.println(line);
            }

综上:在这里插入图片描述

==================================================================================================================================================================

2.4

一、结构框架

在这里插入图片描述

为了解决数组的局限性,引入容器类的概念。
容器的容量"capacity"会随着对象的增加,自动增长
只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

  • 容器类ArrayList

    • 常用方法在这里插入图片描述
package collection;

import java.util.ArrayList;

import character.Hero;

public class TestCollection {

	 public static void main(String[] args) {
		
		 //初始化五个对象
		 //注意:ArrayList类里的每一个对象都是Hero类型,add的也是Hero类型,而不是一个name相同的字符串
		 ArrayList a= new ArrayList();
		 for(int i=0;i<5;i++)
			 a.add(new Hero("hero"+i));
		 
		 //add
         Hero specialhero=new Hero("special hero");
	     a.add(3,specialhero);
	     
	     //整个容器打印
	     System.out.println(a);
	     
	     //contains
	     //判断标准是对象是否相同,而非对象的name值是否相等
	     System.out.println("1:"+a.contains(new Hero("hero1")));//虽然一个新的对象名字也叫 hero 1,但是contains的返回是false
	     System.out.println("2:"+a.contains("hero1"));
	     System.out.println("3:"+a.contains(specialhero));//而对specialHero的判断,contains的返回是true
	     
	     //get(基0)
	     System.out.println("4:"+a.get(5));
	     //System.out.println("5:"+a.get(6));  如果超出了范围,会报错
	     
	     //indexOf
	     System.out.println("6:"+a.indexOf(specialhero));
	     System.out.println("7:"+a.indexOf(new Hero("hero1")));  //没有它,返回-1
	     
	     //remove
	      a.remove(2);  //删除下标是2的对象
	      System.out.println(a);  
	      a.remove(specialhero);  //删除special hero
	      System.out.println(a);  
	      
	      //size
	      System.out.println(a.size()); 
	      
	      //set
	      a.set(2, new Hero("hero2"));
	      System.out.println("8:"+a); 
	 
	      //toArray
	      Hero[]h =(Hero[])a.toArray(new Hero[]{});
	      System.out.println("9:" +h);

	      //把另一个容器里所有的元素,都加入到该容器里来
	       ArrayList anotherHeros = new ArrayList();
	       anotherHeros.add(new Hero("hero a"));
	       anotherHeros.add(new Hero("hero b"));
	       anotherHeros.add(new Hero("hero c"));
	       a.addAll(anotherHeros);
	       System.out.println("10:" +a);
	        
	       
	      //clear
	        a.clear();
	        System.out.println("11:" +a);
	      
	 }
}

在这里插入图片描述
练习-判断集合里是否存在一个 name等于 "hero 1"的对象

ADD:

for (int i = 0; i < heros.size(); i++) {
            Hero h = (Hero) heros.get(i);
            if(name.equals(h.name ) ){
                System.out.printf("找到了name是%s的对象",name);
                break;
            }
        }

关于equals
如果来自同一个类的两个对象,如果没有重写equals方法的逻辑,其结果和equals结果是相同的(是否是同一引用),如果有重写equals方法,则和equals结果可能会不同。
如:Integer就重写了equals,所以其中调用equals表示的是其值是否相同。

  • list接口

import java.util.List;
ArrayList实现了接口List
常用方式接口类引用,指向实例化对象
List heros = new ArrayList();

  • 泛型 Generic

指定了泛型的容器,只能存放指定类型的元素以及其子类,比如都是Hero类,或都是Item类

//引入泛型Generic
        //声明容器的时候,就指定了这种容器,只能放Hero,放其他的就会出错
        List<Hero> genericheros = new ArrayList<Hero>();  //也可以简写成new ArrayList<>();
        genericheros.add(new Hero("盖伦"));
        //如果不是Hero类型,根本就放不进去
        //genericheros.add(new Item("冰杖"));
          
        //除此之外,还能存放Hero的子类
        genericheros.add(new APHero());
         
        //并且在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类
        Hero h = genericheros.get(0);

练习-设计一个ArrayList,使得这个ArrayList里,又可以放Hero,又可以放Item,但是除了这两种对象,其他的对象都不能放

首先创建一个接口 LOL,不需要在其中声明任何方法
接着让Hero和Item都实现LOL接口
最后,声明一个ArrayList的变量lolList,它的泛型是
List lolList = new ArrayList<>();
这样在lolList中就即放Hero对象,又放Item对象了。

  • 迭代器遍历
  • 在这里插入图片描述
 //使用迭代器
       
      //使用while的iterator
        Iterator<Hero> it= heros.iterator();
        //从最开始的位置判断"下一个"位置是否有数据
        //如果有就通过next取出来,并且把指针向下移动
        //直到"下一个"位置没有数据
        while(it.hasNext()){
            Hero h = it.next();
            System.out.println(h);
        }
        
      //使用for的iterator
        for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
            Hero hero = (Hero) iterator.next();
            System.out.println(hero);
        }

===========================================================================================================================

2.5

一、其他集合

  • LinkedList

    LinkedList 实现了List接口
    - import java.util.LinkedList;
    - import java.util.List;
    LinkedList 实现了双向链表结构Deque接口(队列)
    - import java.util.LinkedList;
    - LinkedList ll =new LinkedList();
    - get/removeFirst/Last()
    LinkedList 实现了先进先出Queue接口(队列)
    -import java.util.LinkedList;
    import java.util.Queue;
    - Queue q= new LinkedList();
    - offer 在最后添加元素
    - poll 取出第一个元素
    - peek 查看第一个元素
    Deque 继承 Queue,间接的继承了 Collection

  • 二叉树

    eg:假设通过二叉树对如下10个随机数进行排序
    67,7,30,73,10,0,78,81,10,74
    第一步:插入数据
    插入基本逻辑是,小、相同的放左边,大的放右边在这里插入图片描述
    -第二步是遍历
    我们希望遍历后的结果是从小到大的,所以应该采用中序遍历
    左序即: 中间的数遍历后放在左边
    中序即: 中间的数遍历后放在中间
    右序即: 中间的数遍历后放在右边在这里插入图片描述

package collection;

import java.util.ArrayList;
import java.util.List;

public class Node{
	
   public Node leftnode;
   public Node rightnode;
   public Object value;
	
   //插入数据
   public void add(Object v){
	   
	   if(this.value==null)
		   this.value=v;
	   else{
		   if((Integer)v-(Integer)value<=0){
			   if(leftnode==null)
				   leftnode=new Node();
		       leftnode.add(v);
	      }
		   if((Integer)v-(Integer)value>0){
			   if(rightnode==null)
				   rightnode=new Node();
			   rightnode.add(v);
		  }
		   
	  }
   }
   
   
   //遍历
   public List<Object> traversal(){
	   //容器类t里存放的是所有节点的value。
	   List<Object> t = new ArrayList<>();
	   
	   //中序遍历(按照一定的顺序add到t里)
	   if(this.leftnode!=null)
		   t.addAll(this.leftnode.traversal());
	   
	   t.add(value);
	   
	   if(this.rightnode!=null)
		   t.addAll(this.rightnode.traversal());	   
	   
	   return t;
   }
   
   
   
   public static void main(String[] args) {
	   int a[]=new int []{67, 7, 30, 73, 10, 0, 78, 81, 10, 74};
       Node n =new Node();
	   for(int i:a){
    	   n.add(i);
       }
	   System.out.println(n.traversal());
   }
}

在这里插入图片描述

  • HashMap

    储存数据的方式是—— 键值对

    • import java.util.HashMap;
    • 初始化。put。get。
      eg:HashMap<String,String> dictionary = new HashMap<>();
      dictionary.put(“adc”, “物理英雄”);
      dictionary.put(“adc”);

对于HashMap而言,key是唯一的,不可以重复的。
所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。
不过,同一个对象可以作为值插入到map中,只要对应的key不一样

练习-查找内容性能比较
准备一个ArrayList其中存放3000000(三百万个)Hero对象,其名称是随机的,格式是hero-[4位随机数]
hero-3229
hero-6232
hero-9365

因为总数很大,所以几乎每种都有重复,把名字叫做 hero-5555的所有对象找出来
要求 借助HashMap,找出结果,并统计花费的时间

package collection;
  
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;

  
import character.Hero;
  
public class TestCollection {
    public static void main(String[] args) {
       
    	//初始化Hero对象
       List<Hero> h =new ArrayList<>();
       for (int i = 0; i < 3000000; i++) {
    	   Hero hh = new Hero(   "hero-" + ((int)(Math.random()*9000)+1000));
           h.add(hh);
	   }
       
       //初始化MAP(键值对)(就像字典)
       HashMap<String,List<Hero>> m = new HashMap();
       for(Hero hh:h){//遍历不同的值
    	   //(某值对应着某键) 通过键值对,先获得这个键已有的值集合(就像容器)
    	   List <Hero>list=m.get(hh.name);
    	   if(list==null){//集合不存在则创建新的集合,并加到字典里
    		   list = new ArrayList<>();
               m.put(hh.name, list);
    	   }
    	   list.add(hh);//在值集合上加上该值
       }

       
       //用MAP查找
        long start=System.currentTimeMillis();
        //通过键值对,获得目标键对应的值集合
        List<Hero> aimed=m.get("hero-5555");
        long end=System.currentTimeMillis();
        System.out.println("目标大小:"+aimed.size());
        System.out.println("耗时:"+(int)(end-start));
    }
}
  • HashSet

    不能重复!

    常同于统计”某字符串数组里重复的字符串有多少种“,因为/第二次插入同样的数据,是插不进去的,容器中只会保留一个

    没有按照元素的插入顺序排列!

    • import java.util.HashSet;
    • HashSet numbers = new HashSet();
    • HashSet names = new HashSet();
    • add✔ get× 遍历需要用到迭代器,或者增强型for循环

    HashSet自身并没有独立的实现,而是在里面封装了一个Map.
    HashSet是作为Map的key而存在的
    而value是一个命名为PRESENT的static的Object对象,

ADD:equalsIgnoreCase与equals区别是前者不区分大小写,而后者区分

  • Collection

    • 是个接口
      set不重复,无顺序;list有有,Deque 继承 Queue,间接的继承了 Collection在这里插入图片描述
  • Collections

    -是一个类,容器的工具类
    import java.util.Collections;
package collection;
import java.util.ArrayList;
import java.util.List;


import java.util.Collections;

public class TestCollection{
   public static void main(String[] args) {
	 //初始化集合n
	   List<Integer> n=new ArrayList<>();
	   for (int i = 0; i < 10; i++) {
           n.add(i);
       }
	   //输出集合中的数据
	   System.out.println(n);
	   
	   //翻转reverse
	   Collections.reverse(n);
	   System.out.println(n);
	   
	   //混淆shuffle
	   Collections.shuffle(n);
	   System.out.println(n);
	   
	   //排序sort
	   Collections.sort(n);
	   System.out.println(n); 
	   
	   //交换swap(基0)
	   Collections.swap(n,0,5);
	   System.out.println(n); 
	   
	   //滚动rotate
	   Collections.rotate(n,2);
	   System.out.println(n); 
	   
	   //线程安全化synchronizedList
	   //把非线程安全的List转换为线程安全的List
	   List<Integer> synchronizeda=(List<Integer>)Collections.synchronizedList(n);
  }
}

ADD:
List类的numbers前3位出现3 1 4的代码语言为
(numbers.get(0)==3&&numbers.get(1)==1&&numbers.get(2)==4)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值