Java基础学习第二十九天(装饰者设计模式、SequenceInputStream类、对象的输入输出流对象、Properties类、PrintStream类、编码与解码、转换流、递归)

一、装饰者设计模式

1、举几个例子
需求1:编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有行号。
需求2:编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有分号。
需求3: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有双引号。
需求4: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有行号+ 分号。
需求5: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有分号+ 双引号。
需求6: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有双引号+ 行号。
需求7: 编写一个类拓展BufferedReader的功能, 增强readLine方法返回的字符串带有行号+ 分号+双引号。

class BufferedLineNum extends BufferedReader{
    //行号
    int count = 1 ;
    public BufferedLineNum(Reader in) {
        super(in);
    }
    @Override
    public String readLine() throws IOException {
        String line = super.readLine(); 
        if(line ==null){
            return null;
        }
        line = count+" "+ line;
        count++;
        return line;
    }   
}

//带分号的缓冲输入字符流
class BufferedSemi extends BufferedReader{
    public BufferedSemi(Reader in) {
        super(in);
    }

    @Override
    public String readLine() throws IOException {
        String line =  super.readLine();
        if(line==null){
            return null;
        }
        line = line+";";
        return line;
    }
}

//带双引号的缓冲输入字符流
class  BufferedQuto extends BufferedReader{
    public BufferedQuto(Reader in) {
        super(in);
    }

    @Override
    public String readLine() throws IOException {
        String line = super.readLine();
        if(line==null){
            return null;
        }
        line = "\""+line+"\"";
        return line;
    }
}
public class Demo29.1{
    public static void main(String[] args) throws IOException {
        File file = new File("F:\\Demo1.java");
        //建立数据的输入通道
        FileReader fileReader = new FileReader(file);
        //建立带行号的缓冲输入字符流
        BufferedLineNum bufferedLineNum = new BufferedLineNum(fileReader);

        //带有分号的缓冲输入字符流
        BufferedSemi bufferedSemi = new BufferedSemi(fileReader);

        //带有双引号的缓冲输入字符流
        BufferedQuto bufferedQuto = new BufferedQuto(fileReader);       

        String line = null;
        while((line = bufferedQuto.readLine())!=null){
            System.out.println(line);
        }       
    }   
}

2、装饰者设计模式:增强一个类的功能,而且还可以让这些装饰类互相装饰。

3、装饰者设计模式的步骤:
① 在装饰类的内部维护一个被装饰类的引用。
② 让装饰类有一个共同的父类或者是父接口。

4、继承实现的增强类和修饰模式实现的增强类有何区别?
① 继承实现的增强类
◆ 优点:代码结构清晰,而且实现简单.
◆ 缺点:对于每一个的需要增强的类都要创建具体的子类来帮助其增强,这样会导致继承体系过于庞大。
② 修饰模式实现的增强类
◆ 优点:内部可以通过多态技术对多个需要增强的类进行增强, 可以是这些装饰类达到互相装饰的效果。使用比较灵活。
◆ 缺点:需要内部通过多态技术维护需要被增强的类的实例。进而使得代码稍微复杂。

//带行号的缓冲输入字符流
class BufferedLineNum2  extends BufferedReader{  
    //在内部维护一个被装饰类的引用。
    BufferedReader bufferedReader;
    int count = 1;
    public BufferedLineNum2(BufferedReader bufferedReader){
        super(bufferedReader);// 注意: 该语句没有任何的作用,只不过是为了让代码不报错。
        this.bufferedReader = bufferedReader;
    }

    public String readLine() throws IOException{
        String line = bufferedReader.readLine();
        if(line==null){
            return null;
        }
        line = count+" "+line;
        count++;
        return line;
    }
} 

//带分号缓冲输入字符流
class BufferedSemi2 extends BufferedReader{  //为什么要继承?  是为了让这些装饰类的对象可以作为参数进行传递,达到互相装饰 的效果。
    //在内部维护一个被装饰类的引用。
    BufferedReader bufferedReader;
    public BufferedSemi2(BufferedReader bufferedReader){ // new BuffereLineNum();
        super(bufferedReader);// 注意: 该语句没有任何的作用,只不过是为了让代码不报错。
        this.bufferedReader = bufferedReader;
    }

    public String readLine() throws IOException{
        String line = bufferedReader.readLine();  //如果这里的ReadLine方法是调用了buffereLineNum的readLine方法,问题马上解决。
        if(line==null){
            return null;
        }
        line = line +";";
        return line;
    }
}
//缓冲类带双引号
class BufferedQuto2 extends BufferedReader{
    //在内部维护一个被装饰的类
    BufferedReader bufferedReader;  
    public BufferedQuto2(BufferedReader bufferedReader){  //new  BufferedSemi2();
        super(bufferedReader) ; //只是为了让代码不报错..
        this.bufferedReader = bufferedReader;
    }

    public String readLine() throws IOException{
        String line = bufferedReader.readLine();  //如果这里的ReadLine方法是调用了buffereLineNum的readLine方法,问题马上解决。
        if(line==null){
            return null;
        }
        line = "\""+line +"\"";
        return line;
    }   
}
public class Demo29.2{
    public static void main(String[] args) throws IOException {
        File file = new File("F:\\Demo1.java");
        FileReader fileReader = new FileReader(file);
        //建立缓冲输入字符流
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        //建立带行号的缓冲输入字符流
        BufferedLineNum2 bufferedLineNum = new BufferedLineNum2(bufferedReader);

        //带分号的缓冲输入字符流
        BufferedSemi2 bufferedSemi2 = new BufferedSemi2(bufferedLineNum);

        //带双引号的缓冲输入字符流
        BufferedQuto2 bufferedQuto2 = new  BufferedQuto2(bufferedSemi2);

        String line = null;
        while((line = bufferedQuto2.readLine())!=null){
            System.out.println(line);
        }       
    }   
}

5、练习: 一家三口每个人都会工作,儿子的工作就是画画,母亲的工作就是在儿子的基础上做一个增强,不单止可以画画,还可以上涂料。爸爸的工作就是在妈妈基础上做了增强,就是上画框。

interface Work{
    public void work();
}
class Son implements Work{
    @Override
    public void work() {
        System.out.println("画画...");
    }
}
class Mather implements Work{
    //需要被增强的类
    Work worker;
    public Mather(Work worker){
        this.worker = worker;
    }
    @Override
    public void work() {
        worker.work();
        System.out.println("给画上颜色..");
    }
}
class Father implements Work{
    //需要被增强的类的引用
    Work worker;
    public Father(Work worker){
        this.worker = worker;
    }
    @Override
    public void work() {
        worker.work();
        System.out.println("上画框...");
    }
}
public class Demo29.3{  
    public static void main(String[] args) {
        Son s = new Son();
        s.work();
        Mather m = new Mather(s);
        m.work();
        Father f = new Father(m);
        f.work();
    }
}

二、SequenceInputStream(序列流)

public class Demo29.4{
    public static void main(String[] args) throws IOException {
        merge3();
    }

    //把三个文件合并成一个文件
    public static void merge3() throws IOException{
        //找到目标文件
        File file1 = new File("F:\\a.txt");
        File file2 = new File("F:\\b.txt");
        File file3 = new File("F:\\c.txt");
        File file4 = new File("F:\\d.txt");
        //建立对应的输入输出流对象
        FileOutputStream fileOutputStream = new FileOutputStream(file4);
        FileInputStream fileInputStream1 = new FileInputStream(file1);
        FileInputStream fileInputStream2 = new FileInputStream(file2);
        FileInputStream fileInputStream3 = new FileInputStream(file3);
        //创建序列流对象
        Vector<FileInputStream> vector = new Vector<FileInputStream>();
        vector.add(fileInputStream1);
        vector.add(fileInputStream2);
        vector.add(fileInputStream3);
        Enumeration<FileInputStream> e = vector.elements();
        SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
        //读取文件数据
        byte[] buf = new byte[1024];
        int length = 0; 
        while((length = sequenceInputStream.read(buf))!=-1){
            fileOutputStream.write(buf,0,length);
        }   
        //关闭资源
        sequenceInputStream.close();
        fileOutputStream.close();   
    }

    //使用SequenceInputStream合并文件
    public static void merge2() throws IOException{
        //找到目标文件
        File inFile1 = new File("F:\\a.txt");
        File inFile2 = new File("F:\\b.txt");
        File outFile = new File("F:\\c.txt");
        //建立数据的输入输出通道
        FileOutputStream fileOutputStream = new FileOutputStream(outFile);
        FileInputStream fileInputStream1 = new FileInputStream(inFile1);
        FileInputStream fileInputStream2 = new FileInputStream(inFile2);
        //建立序列流对象
        SequenceInputStream inputStream = new SequenceInputStream(fileInputStream1,fileInputStream2);
        byte[] buf = new byte[1024];
        int length = 0 ;        
        while((length = inputStream.read(buf))!=-1){
            fileOutputStream.write(buf,0,length);
        }
        //关闭资源
        inputStream.close();
        fileOutputStream.close();
    }

    //需求:把a.txt与b.txt 文件的内容合并。
    public static void merge1() throws IOException{
        //找到目标文件
        File inFile1 = new File("F:\\a.txt");
        File inFile2 = new File("F:\\b.txt");
        File outFile = new File("F:\\c.txt");
        //建立数据的输入输出通道
        FileOutputStream fileOutputStream = new FileOutputStream(outFile);
        FileInputStream fileInputStream1 = new FileInputStream(inFile1);
        FileInputStream fileInputStream2 = new FileInputStream(inFile2);    
        //把输入流存储到集合中,然后再从集合中读取
        ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();
        list.add(fileInputStream1);
        list.add(fileInputStream2); 
        //准备一个缓冲数组
        byte[] buf = new byte[1024];
        int length = 0 ;        
        for(int i = 0 ; i< list.size() ; i++){
            FileInputStream fileInputStream = list.get(i);
            while((length = fileInputStream.read(buf))!=-1){
                fileOutputStream.write(buf,0,length);
            }
            //关闭资源
            fileInputStream.close();
        }
        fileOutputStream.close();   
    }
}

1、需求: 把一首mp3先切割成n份,然后再把这些文件合并起来。

public class Demo29.5{
    public static void main(String[] args) throws IOException {
        cutFile();
        mergeFlile();
    }
    //合并
    public static void mergeFlile() throws IOException{
        //找到目标文件
        File dir = new File("F:\\music");
        //通过目标文件夹找到所有的MP3文件,然后把所有的MP3文件添加到vector中。
        Vector<FileInputStream> vector = new Vector<FileInputStream>();
        File[] files = dir.listFiles();
        for(File file : files){
            if(file.getName().endsWith(".mp3")){
                vector.add(new FileInputStream(file));
            }
        }
        //通过Vector获取迭代器
        Enumeration<FileInputStream> e = vector.elements();
        //创建序列流
        SequenceInputStream inputStream = new SequenceInputStream(e);
        //建立文件的输出通道
        FileOutputStream fileOutputStream = new FileOutputStream("F:\\合并.mp3");
        //建立缓冲数组读取文件
        byte[] buf = new byte[1024];
        int length = 0 ; 
        while((length =  inputStream.read(buf))!=-1){
            fileOutputStream.write(buf,0,length);
        }
        //关闭资源
        fileOutputStream.close();
        inputStream.close();    
    }
    //切割MP3
    public static void cutFile() throws IOException{
        File file = new File("F:\\美女\\1.mp3");
        //目标文件夹
        File dir = new File("F:\\music");
        //建立数据的输入通道
        FileInputStream fileInputStream = new FileInputStream(file);
        //建立缓冲数组读取
        byte[] buf = new byte[1024*1024];
        int length = 0;
        for(int i = 0 ;  (length = fileInputStream.read(buf))!=-1 ; i++){
            FileOutputStream fileOutputStream = new FileOutputStream(new File(dir,"part"+i+".mp3"));
            fileOutputStream.write(buf,0,length);
            fileOutputStream.close();
        }
        //关闭资源
        fileInputStream.close();
    }
}

三、对象的输入输出流对象

1、对象的输入输出流:主要的作用是用于写对象的信息与读取对象的信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了
对象的输出流: ObjectOutputStream .
对象的输入流: ObjectInputStream

2、对象输入输出流要注意的细节
① 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
② 对象的反序列化创建对象的时候并不会调用到构造方法的
③ serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
④ 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID进行对比,如果这两个id不一致,那么反序列化就失败了。
⑤ 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
⑥ 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
⑦ 如果一个类维护了另外一个类的引用,那么另外一个类也需要实现Serializable接口。

class Address implements Serializable{
    String country; 
    String city;
    public Address(String country,String city){
        this.country = country;
        this.city = city;
    }
}
class User implements Serializable{
    private static final long serialVersionUID = 1L;
    String userName ;
    String password;
    transient int age;  // transient 透明
    Address address ;
    public User(String userName , String passwrod) {
        this.userName = userName;
        this.password = passwrod;
    }
    public User(String userName , String passwrod,int age,Address address) {
        this.userName = userName;
        this.password = passwrod;
        this.age = age;
        this.address = address;
    }
    @Override
    public String toString() {
        return "用户名:"+this.userName+ " 密码:"+ this.password+" 年龄:"+this.age+" 地址:"+this.address.city;
    }
}
public class Demo29.6{
    public static void main(String[] args) throws IOException, Exception {
        writeObj();
//      readObj();
    }   
    //把文件中的对象信息读取出来-------->对象的反序列化
    public static void readObj() throws  IOException, ClassNotFoundException{
        //找到目标文件
        File file = new File("F:\\obj.txt");
        //建立数据的输入通道
        FileInputStream fileInputStream = new FileInputStream(file);
        //建立对象的输入流对象
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        //读取对象信息
        User user = (User) objectInputStream.readObject(); //创建对象肯定要依赖对象所属 的class文件。
        System.out.println("对象的信息:"+ user);
    }
    //定义方法把对象的信息写到硬盘上------>对象的序列化。
    public static void writeObj() throws IOException{
        //把user对象的信息持久化存储。
        Address address = new Address("中国","广州");
        User user = new User("admin","123",15,address);
        //找到目标文件
        File file = new File("F:\\obj.txt");
        //建立数据输出流对象
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        //建立对象的输出流对象
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        //把对象写出
        objectOutputStream.writeObject(user);
        //关闭资源
        objectOutputStream.close();         
    }
}

四、Properties类

1、 Properties(配置文件类):主要用于生产配置文件与读取配置文件的信息。

2、Properties要注意的细节
① 如果配置文件的信息一旦使用了中文,那么在使用store方法生成配置文件的时候只能使用字符流解决,如果使用字节流生成配置文件的话,默认使用的是iso8859-1码表进行编码存储,这时候会出现乱码。
② 如果Properties中的内容发生了变化,一定要重新使用Properties生成配置文件,否则配置文件信息不会发生变化。

public class Demo29.7{
    public static void main(String[] args) throws IOException {
        creatProperties();  
        //readProperties();
    }

    //读取配置文件的信息 
    public static void readProperties() throws IOException{
        //创建Properties对象
        Properties properties = new Properties();
        //加载配置文件信息到Properties中
        properties.load(new FileReader("F:\\persons.properties"));
        //遍历
        Set<Entry<Object, Object>> entrys = properties.entrySet();
        for(Entry<Object, Object> entry  :entrys){
            System.out.println("键:"+ entry.getKey() +" 值:"+ entry.getValue());
        }
        //修改狗娃的密码
        //把修改后的Properties再生成一个配置文件
        properties.setProperty("狗娃", "007");
        properties.store(new FileWriter("F:\\persons.properties"), "hehe");     
    }

    //保存配置文件文件的信息
    public static void creatProperties() throws IOException{
        //创建Properties
        Properties properties = new Properties();
        properties.setProperty("狗娃","123");
        properties.setProperty("狗剩","234");
        properties.setProperty("铁蛋","345");
        //遍历Properties
        /*Set<Entry<Object, Object>> entrys = properties.entrySet();
        for(Entry<Object, Object> entry  :entrys){
            System.out.println("键:"+ entry.getKey() +" 值:"+ entry.getValue());
        }*/ 

        //使用Properties生产配置文件
        //properties.store(new FileOutputStream("F:\\persons.properties"), "haha"); //第一个参数是一个输出流对象,第二参数是使用一个字符串描述这个配置文件的信息。
        //配置文件信息里面如果有中文
        properties.store(new FileWriter("F:\\persons.properties"), "hehe");
    }
}

3、需求:使用properties实现本软件只能运行三次,超过了三次之后就提示购买正版,退jvm。

public class Demo29.8{
    public static void main(String[] args) throws IOException {
        File file = new File("F:\\count.properties");
        if(!file.exists()){
            //如果配置文件不存在,则创建该配置文件
            file.createNewFile();
        }

        //创建Properties对象
        Properties properties = new Properties();
        //把配置文件的信息加载到properties中
        properties.load(new FileInputStream(file));
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        int count = 0; //定义该变量是用于保存软件的运行次数的。
        //读取配置文件的运行次数
        String value = properties.getProperty("count");
        if(value!=null){
            count = Integer.parseInt(value);
        }

        //判断使用的次数是否已经达到三次
        if(count==3){
            System.out.println("你已经超出了试用次数,请购买正版软件!");
            System.exit(0);
        }
        count++;
        System.out.println("你已经使用了本软件第"+count+"次");
        properties.setProperty("count",count+"");
        //使用Properties生成一个配置文件
        properties.store(fileOutputStream,"runtime");
    }
}

五、PrintStream类

1、打印流(printStream):可以打印任意类型的数据,而且打印数据之前都会先把数据转换成字符串再进行打印。

class Animal{
    String name;
    String color;
    public Animal(String name,String color){
        this.name = name;
        this.color = color;
    }
    @Override
    public String toString() {
        return "名字:"+this.name+ " 颜色:"+ this.color;
    }
}
public class Demo29.9{
    public static void main(String[] args) throws IOException {
        /*FileOutputStream fileOutputStream = new FileOutputStream("F:\\a.txt");
        fileOutputStream.write("97".getBytes());
        fileOutputStream.close();*/

        //打印流可以打印任何类型的数据,而且打印数据之前都会先把数据转换成字符串再进行打印
        File file = new  File("F:\\a.txt");
        //创建一个打印流
        PrintStream printStream = new PrintStream(file);

        printStream.println(97);
        printStream.println(3.14);
        printStream.println('a');
        printStream.println(true);

        Animal a = new Animal("老鼠", "黑色");
        printStream.println(a);

        //默认标准的输出流就是向控制台输出的
        System.setOut(printStream); //重新设置了标准的输出流对象
        System.out.println("哈哈,猜猜我在哪里!!");

        //收集异常的日志信息
        File logFile = new File("F:\\2018年7月11日.log");
        PrintStream logPrintStream = new PrintStream( new FileOutputStream(logFile,true) );
        try{
            int c = 4/0;
            System.out.println("c="+c);
            int[] arr = null;
            System.out.println(arr.length);         
        }catch(Exception e){
            e.printStackTrace(logPrintStream);      
        }   
    }
}

六、编码与解码

1、编码与解码
① 编码: 把看得懂的字符变成看不懂码值这个过程我们称作为编码。
② 解码: 把码值查找对应的字符,我们把这个过程称作为解码。

注意: 以后编码与解码一般我们都使用统一的码表,否则非常容易出乱码。

2、常见的码表

码表 解释
ASCII 美国标准信息交换码,用一个字节的7位可以表示,从-128~127:256
ISO8859-1 拉丁码表、欧洲码表;用一个字节的8位表示。又称Latin-1(拉丁编码)或“西欧语言”。ASCII码是包含的仅仅是英文字母,并且没有完全占满256个编码位置,所以它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,藉以供使用变音符号的拉丁字母语言使用,并且支持德文、法文等。因而它依然是一个单字节编码,比ASCII更全面
GB2312 英文占一个字节,中文占两个字节,是中国的中文编码表
GBK 中国的中文编码表升级,融合了更多的中文文字符号(少数民族)
Unicode 国际标准码,融合了多种文字,所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8 英文占一个字节,中文占三个字节,最多用三个字节来表示一个字符
UTF-16 不管英文中文都是占两个字节

(我们以后接触最多的是iso8859-1、gbk、utf-8)

public class Demo29.10{ 
    public static void main(String[] args) throws Exception {
        String str = "中国";
        byte[] buf = str.getBytes("utf-8");//平台默认的编码表是gbk编码表
        System.out.println("数组的元素:"+Arrays.toString(buf)); 

        str = new String(buf,"utf-8"); //默认会使用gbk码表去解码 
        System.out.println("解码后的字符串:"+ str);        

        String str = "a中国"; // [-2, -1,] 0, 97, 78, 45, 86, -3
        byte[] buf = str.getBytes("unicode"); //编码与解码的时候指定 的码表是unicode实际上就是用了utf-16
        System.out.println("数组的内容:"+ Arrays.toString(buf));

        // 乱码还原 
        String str = "大家好";
        byte[] buf = str.getBytes(); //使用gbk进行编码
        System.out.println("字节数组:"+ Arrays.toString(buf)); 
        str = new String(buf,"iso8859-1");          
        byte[] buf2 = str.getBytes("iso8859-1");
        str = new String(buf2,"gbk"); 
        System.out.println(str);    
    }
}

七、转换流

1、输入字节流的转换流(InputStreamReader)是字节流通向字符流的桥

2、输出字节流的转换流(OutputStreamWriter)可以把输出字节流转换成输出字符流

3、转换流的作用
① 如果目前所获取到的是一个字节流需要转换字符流使用,这时候就可以使用转换流。
② 使用转换流可以指定编码表进行读写文件。

public class Demo29.11{
    public static void main(String[] args) throws IOException {
        //readTest();
        //writeTest();      
        //writeTest2();
        readTest2();
    }

    //使用输入字节流的转换流指定码表进行读取文件数据
    public static void readTest2() throws IOException{
        File file = new File("F:\\a.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        //创建字节流的转换流并且指定码表进行读取
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
        char[] buf = new char[1024];
        int length = 0;
        while((length = inputStreamReader.read(buf))!=-1){
            System.out.println(new String(buf,0,length));
        }   
    }

    //使用输出字节流的转换流指定码表写出数据
    public static void writeTest2() throws IOException{
        File file = new File("F:\\a.txt");
        //建立数据的输出通道
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        //把输出字节流转换成字符流并且指定编码表。
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "utf-8");
        outputStreamWriter.write("新中国好啊");
        //关闭资源
        outputStreamWriter.close(); 
    }   

    public static void writeTest() throws IOException{
        File file = new File("F:\\a.txt");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        //把输出字节流转换成输出字符流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);       
        outputStreamWriter.write("大家好");
        outputStreamWriter.close(); 
    }

    public static void readTest() throws IOException{
        InputStream in = System.in; //获取了标准的输入流
        //read()一次只能读取一个字节
        //System.out.println("读到的字符:"+ (char)in.read());

        //需要把字节流转换成字符流
        InputStreamReader inputStreamReader = new InputStreamReader(in);
        //使用字符流的缓冲类
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line = null;
        while((line = bufferedReader.readLine())!=null){
            System.out.println("内容:"+ line);
        }   
    }
}

八、递归

1、递归:函数的自身调用函数的自身。
递归的使用前提: 必须要有条件的情况下调用

2、需求: 算出5的阶乘5! = 5*4 3 2*1

public class Demo29.12{
    public static void main(String[] args) {
        int result = print(5);
        System.out.println("结果:"+ result);
    }

    public static int print(int num){
        if(num==1){
            return 1;
        }else{
            return num*print(num-1);
        }
    }

    //不用递归
    public static int test(int num){
        int result  =  1;
        while(num>0){
            result = result*num;
            num--;
        }
        return result;
    }
}

3、需求
① 列出一个文件夹的子孙文件与目录
② 列出指定目录中所有的子孙文件与子孙目录名,要求名称前面要有相应数量的空格:第一级前面有0个,第二级前面有1个,第三级前面有2个……,以此类推。
③ 列出指定目录中所有的子孙文件与子孙目录名,要求要是树状结构,效果如下所示:

|--src
|   |--cn
|   |   |--itcast
|   |   |   |--a_helloworld
|   |   |   |   |--HelloWorld.java
|   |   |   |--b_for
|   |   |   |   |--ForTest.java
|   |   |   |--c_api
|   |   |   |   |--Student.java
|--bin
|   |--cn
|   |   |--itcast
|   |   |   |--i_exception
|   |   |   |   |--ExceptionTest.class
|   |   |   |--h_linecount
|   |   |   |   |--LineCounter3.class
|   |   |   |   |--LineCounter2.class
|   |   |   |   |--LineCounter.class
|--lib
|   |--commons-io.jar

④ 删除一个非空的文件夹

public class Demo29.13{
    public static void main(String[] args) {
    /*  File dir = new File("F:\\Demo29");
        listFiles3(dir,"|--");*/    
        File dir = new File("F:\\aa");
        deleteDir(dir);
    }
    //删除了一个非空的目录
    public static void deleteDir(File dir){ 
        File[] files = dir.listFiles(); //列出了所有的子文件
        for(File file : files){
            if(file.isFile()){
                file.delete();
            }else if(file.isDirectory()){
                deleteDir(file);
            }
        }
        dir.delete();
    }
    public static void listFiles3(File dir,String space){ //space 存储的是空格
        File[] files = dir.listFiles(); //列出所有的子文件
        for(File file : files){
            if(file.isFile()){
                System.out.println(space+file.getName());
            }else if(file.isDirectory()){
                System.out.println(space+file.getName());
                listFiles3(file,"|   "+space);
            }
        }
    }

    //列出一个文件夹的子孙文件与目录。
    public static void listFiles2(File dir,String space){ //space 存储的是空格
        File[] files = dir.listFiles(); //列出所有的子文件
        for(File file : files){
            if(file.isFile()){
                System.out.println(space+file.getName());
            }else if(file.isDirectory()){
                System.out.println(space+file.getName());
                listFiles2(file,"  "+space);
            }   
        }   
    }

    //列出一个文件夹的子孙文件与目录。
    public static void listFiles1(File dir){
        File[] files = dir.listFiles(); //列出所有的子文件
        for(File file : files){
            if(file.isFile()){
                System.out.println("文件名:"+file.getName());
            }else if(file.isDirectory()){
                System.out.println("文件夹:"+file.getName());
                listFiles1(file);
            }           
        }           
    }   
}

猜你喜欢

转载自blog.csdn.net/Mr_GaoYang/article/details/80995496