java高级


[TOC]

IO(Input&&Output)

IO流的分类

字符流、字节流

字节和字符

Bit最小的二进制单位,是计算机的操作部分,取值0或者1

Byte(字节)是计算机操作数据的最小单位由8位bit组成 取值(-128~127)

Char(字符)是用户可读的最小单位,在Java里面由16位bit组成 取值(0~65535)

字节流

操作byte类型数据,主要操作类是OutputStream、InputStream的子类;不用缓冲区,直接对文件本身进行操作

字节流

操作字符类型数据,主要操作类是Reader、Writer的子类;使用缓冲区缓冲字符,不关闭流就不会输出任何内容

相互转换

整个IO包实际上分为字节流和字符流;除了这两个流之外还存在一组字节流-字符流的转换类。

OutputStreamWriter:是Writer的子类,将输入的字符流变为字节流,即将一个字符流的输出对象变为以一个字节流输出对象。

public static void m1() throws IOException{
    //字符流转化为字节流输出
    File file=new File("test.txt");
    OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(file));
    osw.write("我是字符流转化为字节流输出的");
    osw.close();
}

InputStreamReader:是Reader的子类,将输入的字节流变为一个字符流,即将一个字节流的输入对象变为字符流的输入对象

public static void m2() throws  IOException{
    File file=new File("test.txt");
    InputStreamReader isr=new InputStreamReader(new FileInputStream(file));
    char[] buf=new char[1024];
    int len=isr.read(buf);
    System.out.println(new String(buf));
    isr.close();
}

File类

java.io.File

路径问题

pathSeparator 路径分隔符 等价于字符串书写的;(windows) :(linux)

String path="D:/aaa.txt"+File.pathSeparator+"G:/bbb.txt"

separator 文件分隔符 等价于字符串写的\\效果

String name="D:"+File.separator+"aaa.txt";
String name2="D://aaa.txt";
String name3="D:\aaa.txt";

windows中的目录分隔符\

​ **使用java代码写的时候,因为\就是转义字符,所以应该写成 \\ 或者也可以写成/ **

Linux中的目录分隔符是/

构造方法

File(File parent,String child)

File(String pathname)

File(String parent,String child)

File(URL uri)

常用的功能

创建删除目录和文件

创建文件 createNewFile()

1、创建过程中如果没有中间的目录就会报错

2、如果文件已经存在了,那么调用方法后的返回值就是false。但是不会报错

3、如果不指定后缀名默认还是创建问价,不会创建文件夹

File file=new File(".\\Demo8_17\\java");
//./java //G:\codes\javaSE\java
//./Demo8_17/java //G:\codes\javaSE\Demo8_17\java
boolean isCraete=file.createNewFile();

创建文件夹功能 mkdir()

1、只能创建一级文件夹,多级目录会报错

2、如果要想创建多级目录需要使用file.mkdirs();

删除文件或者文件夹 delete()

1、如果有中间目录删除会失败,但是并不报错

2、删除文件夹,必须保证该文件夹下没有文件和子文件夹,会删除失败

File的获取功能

  • **file.getName()**通过路径的split(“/“)数组的的最后一个元素

  • **file.getPath()**得到创建file的路径,创建的时候是啥就得到什么

  • **file.getAbsolutePath()**获取绝对路径

  • **file.getAbsoluteFile()**返回的是文件 返回值类型是File

  • file.length()获取的是文件的字节数,返回值:long类型**

  • file.getParent() 获取的是父目录,返回值类型是String

  • file.getParentFile() 获取的是父目录,返回值类型是File类型

    File file=new File(".\\java");
    //./java //G:\codes\javaSE\java
    //./Demo8_17/java //G:\codes\javaSE\Demo8_17\java
    boolean isCraete=file.createNewFile();
    //boolean isdelete =file.delete();
    System.out.println("+++++++++++++");
    
    System.out.println(file.getName());//java
    System.out.println(file.getPath());//.\java  
    System.out.println(file.getAbsolutePath());//G:\codes\javaSE\.\java
    System.out.println(file.getAbsoluteFile());//G:\codes\javaSE\.\java
    System.out.println(file.length());//0
    System.out.println(file.getParent());//.
    System.out.println(file.getParentFile());//.

File判断功能

  • **file.exists()**判断路是否存在

  • file.isDirectory() 如果文件夹路径存在,并且路径表示的是文件夹的时候才会返回true

  • file.isFile() 如果文件路径存在,并且是文件才会返回true

    //File判断功能  java文件没有创建
    //判断路径是否存在
    File file1=new File(".//java");
    boolean exists=file.exists();
    System.out.println(exists);//true
    //判断是不是文件夹
    boolean isDirectory=file.isDirectory();
    System.out.println(isDirectory);//false
    //判断是否是文件
    boolean isFile=file.isFile();
    System.out.println(isFile);//true

遍历目录

//遍历目录 返回字符串数组  .
File file2=new File("G:\\codes\\javaSE");
String[] list=file2.list();
System.out.println(Arrays.toString(list));// Demo8_17,Demo8_9  输出该文件加目录下的所有子目录的名字
//遍历目录 返回File数组
File[] files=file2.listFiles();
System.out.println(Arrays.toString(files));//返回从磁盘目录开始的地址
//获取磁盘根目录
File[] files1=file2.listRoots();
System.out.println(Arrays.toString(files1));//返回本地电脑所有的磁盘号

文件过滤器

通过设置可以获取指定类型的文件

//文件过滤器
//通过重写抽象方法 FileFilter 当返回值为true的时候,添加进数组,实现文件过滤的功能
//原理: file.listFiles()每次获取到一个文件夹都会调用对应接口的accept方法
//accept()方法如果返回值为true,就将此文件或者文件夹放入对应的数组中,所以我们可以用我们的代码进行控制函数的返回值是true还是false
File file3=new File("G:\\codes\\javaSE");
file.listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        return false;
    }
});

输入流和输出流

输出流和输入流是相对于程序而言。当需要将磁盘中的文件读入程序中,这种流这就是—-输入流;当需要将数据从程序写入到文件这种流就是—-输出流

输出流 OutputStream

OutputStream叫做字节输出流,每次操作的是1个字节(8位)

OutputStream可以写入任何文件

OutputStream抽象类是所有输出字节流类的超类

操作的数据都是字节

方法摘要

FileOutputStream

文件输出流,用于将程序当中的数据写入File的输出流

构造方法

append 设置为true/false,表示是否附加写入的数据

参数解释
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(FileDescriptor fdpj) 创建文件输出流以写入指定的文件描述符
FileOutputStream(String name) 创建文件输入流以指定的名称写入文件
FileOutputStraem(String name,boolean append) 创建文件输出流以指定的名称写入文件

写入方式

System.out.println("-------------输出流");
FileOutputStream fos=new FileOutputStream("G:\\codes\\javaSE\\Demo8_17\\src\\demo\\demo.txt",true);
fos.write(123);
fos.write("\r\n".getBytes());
fos.write(new byte[]{65,66,67});
fos.write("\r\n".getBytes());
fos.write(new byte[]{68,69,70},0,2);
fos.write("\r\n".getBytes());
fos.write("大家好".getBytes());
//如果需要多次写入并且进行换行操作的话需要添加  \r\n

写的结果

InputStream

跟OutputStream一样,每次操作的是一个字节(8位)

InputStream可以读取任何文件

InputStream 抽象类,是表示输入字节流的所有类的超类

基本共性功能方法

int read():读取一个字节并且返回,没有字节返回-1

int read(byte[] );读取一定量的字节数,并且存储到字节数组中

void close(): 关闭输入流

FileInputStream

文件输入流,用于数据读取到程序的输入流,用于数据读取到程序的输入流

构造方法

FileInputStream(File file) 打开与实际文件的连接创建一个FileInputStream,传入一个File对象
FileInputStream(String name) 传入一个路径名name

读取方法

//读取单个字节
// read的返回值代表读取的内容文件末尾的值是-1
File file=new File("./Demo8_17/src/demo/demo.txt");
System.out.println(file.exists());
FileInputStream fis=new FileInputStream(file);
int len=0;
while((len = fis.read()) != -1){ System.out.print((char)len); }
fis.close();

//////////////
//读取到字节数组
//返回值代表读取到了多少个有效的字节
读取的内容在byte里边
File file1 = new File("./Demo8_17/src/demo/demo.txt");
FileInputStream fis1 = new FileInputStream(file);
byte [] b = new byte[2];
len = 0;
while((len = fis.read(b)) != -1){
    String str = new String(b, 0, len);
    System.out.print(str);
}

字符流

字符流操作的单位也是以字节为单位,其主要就是为了解决文本文件的输入和输出

Writer

字符输出流,和字节流的主要区别在与字符流只能操作文本文件

abstract void close() 关闭流,先刷新
abstract void flush() 刷新流
void write(char[] cbuf) 写入一个字符数组
abstract void write(char[] cbuf,int off,int len)写入字符数组的一部分
void write(int c)写一个字符
void write(String str) 写入一个字符串
void write(String str,int off ,int len) 写入一字符串的一部分

FileWrite

Write的实现类

构造方法

append的作用 true/false 设置为false,表示从头开始写

FileWriter(File file) 给一个File对象构造一个FileWriter对象
FileWriter(File file,boolean append)
FileWriter(String name)
FileWriter(String name,boolean append)
FileWrite(FileDescriptor fd) 构造与文件描述符关联的FileWrite对象

注意

它中的构造方法都是super(new FileOutputStream(fileName));

Reader

字符输入流,和字节流的区别在与字符流只能操作文本文件

方法

read()读取一个字符 ;read(char[] cbuf) 将字符读入数组

FileReader

构造方法

FileReader(File file)
FileReader(String fileName)
FileReader(FileDescriptor fd)

使用方法

public class HelloWorld {
    public static void main(String [] args) throws IOException {
        FileReader fr = new FileReader("./Demo8_18/src/com/lyz/onclass/test.txt");
        FileWriter fw = new FileWriter("./Demo8_18/src/com/lyz/onclass/newtest.txt");
        char [] ch = new char[1024];
        int len = 0;
        while((len = fr.read(ch)) != -1){
            fw.write(ch, 0, len);
        }
        fr.close();
        fw.close();
    }
}
test.txt newtest.txt
test.txt newtest.txt

注意

使用字符流,不能去读取不是文本的文件

转换流

正常情况下idea的开发环境的编码为UTF-8的时候,那么当我们使用字节流或者字符进行写入文件的时候,文件编码也还是UTF-8,如果用字节流或者字符流进行文件的读取的操作的时候,默认也是UTF-8格式

如果文件时GBK的话,就会出现乱码情况

转换流的出现,就是为了解决写入和读取文本文件的时候指定具体的编码格式

OutputStreamWriter

OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的字符编码表,将要写入流中的字符编码成字节

  • 创建一个字节输出流 FileOutputStream fos=new FileOutputStream(“”)
  • 创建转换输出流,绑定指定的字节输出流,并指定编码格式OutputStreamWriter osw =new OutputStreamWriter(fos,”gbk”)
  • 调用write方法写入 osw.write(“你好”)

InputStreamReader

InputStreamReader是字节流通向字符流的桥梁,它使用指定的编码表读取字节并将其解码为字符

  • 创建一个字节输入流FileInputStream fis=new FileInputStream(“”)
  • 创建转换输入流,绑定字节输入流,并指定编码格式IntputStreamReader isr=new InputStreamReader(fis,”UTF-8”)
  • 调用read方法进行读取

补充: 字符编码

UTF-8的编码格式

缓冲流

1、缓冲流 :BufferReader BufferWriter BufferInputStream BufferOutputStream

2、作用:提高读取和写入的速度

​ 提高读取和写入的速度的原因:内部提供了一个大小为8192的缓冲区

3、处理流,就是套接在已有流的基础上

BufferInputStream BufferOutputStream

//缓冲流处理流的一种,提高流的读取和写入的速度
//主要是在内部提供了一个缓冲区
public static void bufferTest1() {

    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        //1、造文件
        File srcPath = new File("./src/古典雅致美女打着纸伞3840_2160.jpg");
        File destPath = new File("./src/01.jpg");
        //2、造流
        FileInputStream fis = new FileInputStream(srcPath);
        FileOutputStream fos = new FileOutputStream(destPath);
        //造处理流  即缓冲流
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);

        //复制的操作  读取和写入

        byte[] buffer = new byte[10];
        int len;
        while ((len = bis.read(buffer)) != -1) {
            bos.write(buffer);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //资源关闭
        //要求,先关闭外层的流,再关闭内层的流
        if(bis!=null){
            try {
                bis.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        if(bos!=null){
            try {
                bos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        //关闭外层流的同时,内层流也会自动的关闭
        //关闭内层的代码可以省略
        /*fis.close();
        fos.close();*/
    }
}
public static void FileInputOutputStreamTest(String src,String dest) {
    File srcPath = new File(src);
    File destPath = new File(dest);
    FileInputStream fis = null;
    FileOutputStream fos=null;
    try {
        fis = new FileInputStream(srcPath);
        fos = new FileOutputStream(destPath);
        byte[] b = new byte[10];
        int len;
        while ((len = fis.read(b)) != -1) {
            fos.write(b);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        if(fos!=null) {
            try {
                fos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public static void main(String[] args) {
    String src="./src/古典雅致美女打着纸伞3840_2160.jpg";
    String dest="./src/02.jpg";
    long start1=System.currentTimeMillis();
    bufferTest1();
    long start2=System.currentTimeMillis();
    FileInputOutputStreamTest(src,dest);
    long end=System.currentTimeMillis();
    System.out.println("前"+(start2-start1)+"   后"+(end-start2));
}

//前55   后2294

**BufferReader BufferWriter **

为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度

BufferedReader和BufferedWriter类各拥有8192个字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。

 public static void testBufferedReaderBufferedWriter() throws IOException{
        BufferedReader br=new BufferedReader(new FileReader(new File("./src/demo.txt")));
        BufferedWriter bw=new BufferedWriter(new FileWriter(new File("./src/demo_copy.txt")));
/*
//方式一,和之前一样使用 char[]数组
        char[] ch=new char[1024];
        int len;
        while((len=br.read(ch))!=-1){
            bw.write(ch);
        }
*/

        //还有一种方式 使用String  raedLine
        String data;
        while((data=br.readLine())!=null){
            System.out.println(data);
            bw.write(data);
        }
        br.close();
        bw.close();
    }

BufferedReader中的两种读取方法的对比

源文件 String data br.readLine(data)!=null char[] ch (len=br.read(ch))!=-1
image-20220820110241120 image-20220820110140753 image-20220820110511650
中间没有不读取换行符号,遇到换行符号就直接返回读取到的一行数据 能读取到换行符

注意: 在BufferedReader 和BufferedWriter中有一个flush()方法,调用时,不管缓冲区有多少内容都直接返回出来,默认是缓冲区满和没有了才返回

Other

1、标准的输入输出流

​ 1.1

​ System.in:标准的输入流,默认从键盘输入 类型是InputStream

​ System.out:标准的输出流,默认从控制台输出

​ 1.2:重定向

​ System类的setIn(InputStream is)/setOut(PrintStream ps)方式重新指定输入和输出的

2、打印流

3、数据流

1单纯使用System.in实现程序的输入

public static void m1() throws IOException {
    //输入    使用 Scanner实现,调用next()返回一个参数
    //        使用System.in实现   System.in --->BufferedReader的readLine();
    InputStreamReader is=new InputStreamReader(System.in);
    BufferedReader br=new BufferedReader(is);
    while(true){
        String data=br.readLine();
        if("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
            System.out.println("程序输入即将结束");
            break;
        }
        System.out.println(data.length()+"|-------|"+data);
    }
}

Properties

Properties类表示一个持久的属性集。Properties可以保存在流中或从流中加载。属性列表中每一个键即其对应的值都是一个字符串。

特点

  1. Hashtable(和HashMap不同的是,Hashtable是一个线程安全的)的子类,map集合中的方法都是可以用的
  2. 该集合没有泛型,键和值都只能是字符串
  3. 他是一个可以持久化的属性集
  4. 它主要使用getProperty没有设置synchronized , setProperty设置类synchronized获取和改变他的值

常用方法

  • void load(InputStream inStream) 从输入字节流读取属性列表
  • void load(Reader reader) 从输入字符流读取属性列表(关键字和元素对)
  • void store(OutputStream out,String comments) 将此属性列表(key和value)写入Properties表中,亦适用于使用load(InputStream)方法加载到Properties表中的格式输出流
  • void store(Writer writer,String comments) 使用load(Reader)方法的格式输出到输出字符流

多线程

方式一:

进程概念

进程:进程值正在运行的程序。确切的说,当一个程序加载到内存中运行的时候,即变成了一个进程,进程时处于运行过程中的程序,具有一定的独立功能

线程: 线程时进程的一个执行单元,负责当前进程中程序的运行 一个进程至少有一个线程,可以有多个线程的

总结: 一个程序至少有一个进程,一个进程中可以包含多个线程

多线程开发

多线程开发

线程的调度

有两种调度模式:分时调度模型和抢占式调度模型

分时调度:所有线程轮流使用cpu的使用权,平均分配每一个线程占用的CPU的时间

抢占式调度:优先让优先级高的线程使用CPU

通过继承Thread类来实现多线程

Thread的构造方法

不能直接调用线程中的run()方法,要调用线程的start()函数,才能实现多线程

一个线程只能启动一次,要不然就会报错误IllegalThreadStateException();

实例:

class MyThread extends Thread{
    private String title;
    public MyThread(String title){
        this.title=title;
    }

    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println(this.title+"i的值是"+i);
        }
    }
}

基于Runable接口实现多线程

Runable实现多线程

Thread runable1=new Thread(new MyThread_R("线程R1"));
runable1.start();

利用lambda表达式来创建线程,在以后的开发对于多线程的实现,优先考虑的就是Runable接口的实现,

for(int x=0;x<3;x++){
    String title="线程对象-"+x;
    new Thread(()->{
        for(int y=0;y<10;y++){
            System.out.println( title+"lambda表达式"+y);
        }
    }).start();
}

Thread与Runable关系

使用Runable是最方便的,可以避免单继承的局限,也可以更好的对功能进行扩充

但是从结构上来看,之前继承Thread的时候,还是实现Runable接口中的run()方法

通过 Thread 类的构造方法传递了一个 Runnable 接口对象的时候,那么该接口对象将被 Thread 类中的 target 属性所保存,在 start() 方法执行的时候会调用Thread类中的 run() 方法,而这个 run() 方法去调用 Runnable 接口子类被覆写过的 run() 方法。

多线程开发的本质上是多个线程可以进行同一资源的抢占

public class Thread implements Runnable 

Thread和Runable的关系

Callable接口实现多线程

Runable接口有一个问题,就是线程结束之后无法获取一个返回值,jdk1.5之后就提出了java.util.concurrent.Callable,就解决了这一问题

Callable定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型,这样的好处就是 可以避免一些安全隐患

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 为什么说Callable接口的方式创建多线程比实现Runable接口创建多线程强大?
 * 1、call() 可以有返回值
 * 2、call() 可以抛出异常,被外面的操作捕获,获取异常信息
 * 3、call()是支持泛型的,指明泛型的时候:1、重写的类需要明确泛型,2、创建的FutureTask对象也需要指明泛型
 */

public class ThreadNew {
    public static void main(String[] args) {
        MyCallMyThread thread=new MyCallMyThread();
        FutureTask futureTask = new FutureTask(thread);
        new Thread(futureTask).start();
        try {
            //get()返回值为FutureTask构造器参数Callable实现类重写的call()的返回值。
            //如果对call的返回值不感兴趣或者说,返回值并没有什么实际效果,那就不用调用get()方法
             int sum=(int) futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

    }
}
class MyCallMyThread implements Callable{
    @Override
    public Object call() throws Exception {
        int sum=0;
        for(int i=1;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}

线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 创建线程的方式四:使用线程池
 *
 * 好处:
 *      1、提高响应速度(减少创建新线程的时间)
 *      2、减低资源消耗(重复利用线程池中的线程,不需要每次都创建
 *      3、便于线程管理
 *          corePoolSize():核心池的大小
 *          maximumPoolSize():最大线程数
 *          keepAliveTime():线程没有任务时最多保持多长时间后会终止
 */

class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+"   "+i);
            }
        }
    }
}


class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName()+"   "+i);
            }
        }
    }
}

public class ThreadPool {
    public static void main(String[] args) {
        //1、提供指定线程数量的线程池
        ExecutorService service=Executors.newFixedThreadPool(10);

        //设置线程池的属性
        //ExecutorService 是一个线程
        System.out.println(service.getClass());
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //service1.set

        //2、执行指定的线程操作、需要提供实现Runable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合使用于Runable;
        service.execute(new NumberThread1());//适合使用于Runable;
        //service.submit();//适合使用于Callable;
        //3、关闭连接池
        service.shutdown();
    }
}

设置线程池的属性

序列化

序列化操作的对象必须实现Serializable接口

读取末尾 抛出EOFException

序列化流没有续写::增删改必须 全部读取,修改后,再覆盖

重点

Runable和Callable的区别

1、Runable是在jdk1.0的时候提出的多线程的实现接口,而Callable实在jdk1.5之后提出的

2、java.long.Runable接口中只提供了一个run()方法,并且没有返回值,就无法判断该线程是否已经结束

3、java.util.concurrent.Callable接口提供了call()方法,可以有返回值

创建多线程有几种方式 四种方法

1、继承Thread类

2、基于Runable接口实现

3、Callable接口实现多线程

4、使用线程池


文章作者: 毛豆不逗比
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 毛豆不逗比 !
  目录
{% include '_third-party/exturl.swig' %} {% include '_third-party/bookmark.swig' %} {% include '_third-party/copy-code.swig' %} + {% include '_custom/custom.swig' %}