Java起步

网络

文章目录

  • 网络
  • 网络通信要素
      • 要素
          • 协议说明
  • Java之网络通信Socket
      • 说明
        • UDP的端点服务对象
        • TCP服务对象
          • 实例应用SocketTCP
  • Java之数据报包——DatagramPacket
      • 说明
      • 构造方法
  • Java之UDP的端点服务对象——DatagramSocket
      • 说明
      • 示例
    • 进阶应用之多线程
  • Java之TCP服务——Socket/ServerSocket
      • 说明
    • Socket
            • 构造方法说明
        • 其他重要提示
  • Java之Socket/ServerSocket为基础的C/S文件上传示例
    • 需求
      • 客户端源码
      • 服务端
  • Java之统一资源定位符——URL
      • 说明
            • URI、URL 和 URN
      • URL类中的常用方法
          • 方法注释:
      • URLConnection类
  • io
  • Java IO之键盘录入
  • Java File对象的常用方法
      • 判断方法
      • 获取文件信息方法
      • 文件列表与筛选
      • 文件操纵方法
  • File 之文件遍历与删除
      • 文件遍历
      • 文件删除
  • Java Properties类
      • 配置文件说明
      • Properties说明
        • 常用方法
            • Properties对象操作方法
            • 持久化存取方法
  • 实际使用过程中流对象的选择
        • 装饰设计模式扩展功能与继承模式扩展功能区别:
    • 流的操作规律:
    • 需求示例演示:
  • Print类详解
      • PrintStream:字节打印流说明
        • 注意
      • 常用函数
      • PrintWriter:字符打印流。
      • 自动刷新功能
  • SequenceInputStream 序列流
      • 说明
  • IO流之综合操作——文件的切割与合并
  • Java之序列流ObjectOutputStream/ObjectInputStream
      • 说明
      • 演示
  • Java之随机访问文件—RandomAccessFile
      • 说明
      • 构造函数参数
      • 常用函数说明
      • 其他
  • Java之管道流PipedInputStream/PipedOutputStream
      • 说明
      • 代码示例
  • Java之数据流DataInputStream/DataOutputStream
      • 说明
            • 注意:
  • Java之字节数组流ByteArrayInputStream/ByteArrayOutputStream
      • 说明
  • 反射
      • 获取一个类的Class对象方式
      • 获取不同类型的class
      • 类的加载
      • 根据一个类的Class对象获取其内部信息
      • 使用通过反射获得的数据
      • 使用反射获取注解
  • 内部类
      • 说明
      • 内部类与外部类成员变量的访问
      • 局部内部类
            • 局部内部类示例:
      • 匿名内部类
  • 枚举
      • 枚举类的使用
      • 如何定义枚举类
      • Enum类中的常用方法:
      • 使用enum关键字定义的枚举类实现接口的情况
      • 使用enum关键字定义枚举类
  • 按字节截取字符串

网络通信要素

要素

  1. 网络层:
    IP地址:InetAddress
     • 网络中设备的标识
    传输协议
     • 通讯的规则
     • 常见协议: TCP, UDP

  2. 传输层:
    端口号
     • 用于标识进程的逻辑地址,不同进程的标识
     • 有效端口:0-65535,其中0~1024系统使用或保留端口。

协议说明
  1. UDP
    • 将数据及源和目的封装成数据包中,不需要建立连接
    • 每个数据报的大小在限制在64k内
    • 因无连接,是不可靠协议
    • 不需要建立连接,速度快
  2. TCP
    • 建立连接,形成传输数据的通道。
    • 在连接中进行大数据量传输
    • 通过三次握手完成连接,是可靠协议
    • 必须建立连接,效率会稍低

Java之网络通信Socket

说明

  • Socket就是为网络服务提供的一种机制。
  • 通信的两端都有Socket。
  • 网络通信其实就是Socket间的通信。
  • 数据在两个Socket间通过IO传输。

其实面向网络编程就是面向Socket编程,有关的Socket详细介绍链接:Socket的一些说明

UDP的端点服务对象

DatagramSocket详解

TCP服务对象

Socket/ServiceSocket详解

实例应用SocketTCP

文件传输演示

Java之数据报包——DatagramPacket

说明

  1. DatagramPacket类表示数据报包。与DatagramSocket联合使用
  2. 数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。 (面向无连接)

构造方法

  期初看官方文档挺晕的,构造函数挺多的,不知道那些事用于发送和接收的,但有个规律就是,凡是发送的构造方法都接收InetAddress对象。

发送包构造方法 说明
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
接收包构造方法 说明
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int offset, int length) 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。

Java之UDP的端点服务对象——DatagramSocket

说明

在介绍DatagramSocket时还需要了解DatagramPacket类说明

  1. DatagramSocket类表示用来发送接收数据报包的Socket。
  2. DatagramSocket启用 UDP 广播发送.

示例

  1. 建立UDP发送端:
    (1)建立updsocket服务。
    (2)提供数据,并将数据封装到数据包中。
    (3)通过socket服务的发送功能,将数据包发出去。
    (4)关闭资源。
class UdpSend {
    public static void main(String[] args) throws Exception {
        //1,创建udp服务。通过DatagramSocket对象。
        DatagramSocket ds = new DatagramSocket(8888);  //发送端的源端口号

        //2,确定数据,并封装成数据包。
        byte[] buf = "This is UDP-encapsulated data".getBytes();
        //使用的构造函数是:
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        DatagramPacket dp =new DatagramPacket(buf, buf.length, 
                InetAddress.getByName("192.168.0.106"), 10000);//10000为目的端口号
        //3,通过socket服务,将已有的数据包发送出去。通过send方法。
        ds.send(dp);
        //4,关闭资源。
        ds.close();
    }
}

  1. 定义udp的接收端。
    (1)定义udpsocket服务。通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识。
    方便于明确哪些数据过来该应用程序可以处理。
    (2)定义一个数据包,因为要存储接收到的字节数据。
    因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
    (3)通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
    (4)通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。
    (5)关闭资源。
class UdpRece {
    public static void main(String[] args) throws Exception {
        //1,创建udp socket,建立端点。
        DatagramSocket ds = new DatagramSocket(10000);//接收端口号
        while (true) {
            //2,定义数据包。用于存储数据。
            byte[] buf = new byte[1024];
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            //3,通过服务的receive方法将收到数据存入数据包中。
            ds.receive(dp);//阻塞式方法。
            //4,通过数据包的方法获取其中的数据。
            String ip = dp.getAddress().getHostAddress();
            String data = new String(dp.getData(), 0, dp.getLength());
            int port = dp.getPort();
            System.out.println(ip + "::" + data + "::" + port);
        }
        //5,关闭资源
        ds.close();
    }
}

进阶应用之多线程

  1. 启动程序类
import java.net.DatagramSocket;
import java.net.SocketException;

public class Main {
    public static void main(String[] args) {
        try {
            Service service = new Service(new DatagramSocket(10000));
            Client client = new Client(new DatagramSocket(20000));

            new Thread(service).start();
            new Thread(client).start();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}

  1. 接收服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Service implements Runnable {

    DatagramSocket ds = null;

    public Service(DatagramSocket ds) {
        System.out.println("接收端开启....");
        this.ds = ds;
    }

    @Override
    public void run() {
        byte[] buf = new byte[1024];

        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        try {
            while (true) {
                ds.receive(dp);
                String hostName = dp.getAddress().getHostName();
                int port = dp.getPort();
                int length = dp.getLength();

                System.out.println("hostName:" + hostName + "port:" + port + '\n' +
                        new String(buf, 0, length));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ds.close();
        }
    }
}

3.数据发送端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Client implements Runnable {
    DatagramSocket ds = null;
    BufferedReader br = new BufferedReader(
            new InputStreamReader(System.in));
    byte[] buf = new byte[1024];

    public Client(DatagramSocket ds) {
        System.out.println("发送端开启....");
        this.ds = ds;
    }


    @Override
    public void run() {
        try {
            String str = null;
            while ((str = br.readLine()) != null) {
                buf = str.getBytes();
                DatagramPacket dp = new DatagramPacket(buf, 0,buf.length,
                        InetAddress.getByName("192.168.0.106"),10000);
                ds.send(dp);
                if("886".equals(str)){
                    System.out.println("接收端关闭");
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            ds.close();
        }
    }
}

Java之TCP服务——Socket/ServerSocket

说明

  java中TCP与UDP不同,UDP的DatagramSocket类既可以接收也可以发送,而TCP传输分为客户端Socket类和服务端ServerSocket类。
  不了解UDP的朋友请参考这篇文章

Socket

  1. Socket类实现客户端套接字。 套接字是两台机器间通信的端点。
  2. Tcp传输,客户端建立的过程。
    (1)创建tcp客户端socket服务。使用的是Socket对象。建议该对象一创建就明确目的地。要连接的主机。
    (2)如果连接建立成功,说明数据传输通道已建立。该通道就是socket流 ,是底层建立好的。 既然是流,说明这里既有输入又有输出。想要输入或者输出流对象,可以找Socket来获取。可以通过getOutputStream()和getInputStream()来获取两个字节流。
    (3)使用输出流,将数据写出。
    (4)关闭资源。

客户端测试程序

public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("192.168.0.106"),10000);
        OutputStream outputStream = socket.getOutputStream();
        //发送数据给服务端,后面加\r\n是给服务端的readLine方法一个结束标记,否则会出现程序阻塞
        outputStream.write("测试数据\r\n".getBytes());
        
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
        String s = bufferedReader.readLine();
        System.out.println(s);
        socket.close();
    }

  1. 建立tcp服务端的思路:
    (1)创建服务端socket服务。通过ServerSocket对象。
    (2)服务端必须对外提供一个端口,否则客户端无法连接。
    (3)获取连接过来的客户端对象。
    (4)通过客户端对象获取socket流读取客户端发来的数据
    并打印在控制台上。
    (5)关闭资源。关客户端,关服务端。

服务端测试程序

  public static void main(String[] args) throws IOException {
  		//服务端在监听一个号为10000的端口
        ServerSocket serverSocket = new ServerSocket(10000);
        //阻塞式方法,用于接收客户端发来的Socket对象
        Socket accept = serverSocket.accept();
        //服务端本身没有流,流是从客户端获取的(拿着客户端的Socket获取流)
        BufferedReader bufferedReader = new BufferedReader(
        						new InputStreamReader(accept.getInputStream()));
        String s = bufferedReader.readLine();
        String ip = accept.getInetAddress().getHostName();
        System.out.println("ip:"+ip+s);
        
		//给客户端一个回执信息
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write("你好客户端,我收到了\r\n".getBytes());
        serverSocket.close();
    }

构造方法说明
构造方法名称 说明
Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字,后期可通过connect方法进行指定连接。

其他重要提示

  一定要留意流中的阻塞式方法,如果使用不当就会导致程序挂起,如缓冲区没有刷新或给对方发送的数据没有结束标记等。因此java提供了两个方法用于告诉对方,本方已经结束的,你可以关闭进行下一步操作了。

方法名称 说明
shutdownInput() 此套接字的输入流置于“流的末尾”。
shutdownOutput() 禁用此套接字的输出流。

Java之Socket/ServerSocket为基础的C/S文件上传示例

需求

  • 需求:把客户端指定的文件上传到服务器

(自己随便写的,有些地方需要优化)


客户端源码

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
    public static void main(String[] args) {
        //源文件
        File Pic = new File("F:\\u盘\\xb_4K.mp4");

        Socket socket = null;

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        BufferedInputStream sbis = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(Pic));
            //
            socket = new Socket(InetAddress.getByName("192.168.0.106"), 60000);

            bos = new BufferedOutputStream(socket.getOutputStream());


            //传递文件名称
            //注意此处bug,传递过去的是一个绝对路径,因此需要切割
            String name = Pic.getName()+"\r\n";
            if (name.contains("\\")) {
                int i = name.lastIndexOf("\\");
                name = name.substring(i);
            }
            bos.write(name.getBytes());
            bos.flush();

            //传递数据
            int len = 0;
            while ((len = bis.read()) != -1) {
                bos.write(len);
            }
            bos.flush();
            socket.shutdownOutput();

            //接受服务端返回
            sbis = new BufferedInputStream(socket.getInputStream());
            byte[] buff = new byte[1024];
            int read = sbis.read(buff);
            String s = new String(buff, 0, read);
            System.out.println(s);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

服务端

  • 接收客户端传来的文件,并保存到服务器指定的文件夹中。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

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

        String filename = null;

        ServerSocket serverSocket = null;

        File putFile = null;

        try {
            //1.创建Socket服务设置监听端口,获取服务端Socket对象
            serverSocket = new ServerSocket(60000);
            Socket accept = serverSocket.accept();
            //2.获取读取流
            InputStream is = accept.getInputStream();
            //3.获取读取流的第一个字段(第一个字段为文件名称)
            BufferedReader read = new BufferedReader(new InputStreamReader(is));
            filename = read.readLine();
            putFile = new File("E:\\IO测试文件夹\\" + filename);

            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(putFile));

            BufferedInputStream bis = new BufferedInputStream(is);
            int len = 0;
            while ((len = bis.read()) != -1) {
                bos.write(len);
            }
            bos.flush();
            accept.shutdownInput();

            PrintWriter pw = new PrintWriter(new OutputStreamWriter(accept.getOutputStream()),true);
            pw.println("上传成功");

            accept.shutdownOutput();

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
			try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
		}
    }
}

多线程改进版

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

class Demo{
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket;

        serverSocket = new ServerSocket(60000);
        while (true) {
            //来一个任务就开启一个线程。
            new Thread(new ThreadServerPic(serverSocket.accept())).start();
        }
    }
}


public class ThreadServerPic implements Runnable {
    private String fileName = null;
    private File putFile = null;
    private Socket accept;

    public ThreadServerPic(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        try {
            InputStream is = accept.getInputStream();
            //3.获取读取流的第一个字段(第一个字段为文件名称)
            BufferedReader read = new BufferedReader(new InputStreamReader(is));
            fileName = read.readLine();
            putFile = new File("E:\\IO测试文件夹\\" + fileName);
            if (putFile.exists()) {
                putFile = new File(putFile.getName() + "(1)");
            }
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(putFile));

            BufferedInputStream bis = new BufferedInputStream(is);
            int len = 0;
            while ((len = bis.read()) != -1) {
                bos.write(len);
            }
            bos.flush();
            accept.shutdownInput();

            PrintWriter pw = new PrintWriter(new OutputStreamWriter(accept.getOutputStream()), true);
            pw.println("上传成功");

            accept.shutdownOutput();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                accept.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Java之统一资源定位符——URL

说明

  统一资源定位符是指向互联网“资源”的指针。如:https://me.csdn.net/qq_39711439 ,通过这个标识符可在网络中定位资源。此类处于应用层,底层封装了Socket,因此在处理浏览器等方面的信息,应使用本类。
  而Java中的URL类是对如同【https://blog.csdn.net/qq_39711439/article/details/100859357】这种网络地址的抽象。这个类中有一些方法可以获取这个字符串中的信息,如协议,域名,端口等。下面会介绍一些常用方法。

URI、URL 和 URN

  URI 是统一资源标识符,而 URL 是统一资源定位符。URL 和 URN 都是 URI 的子集

URL类中的常用方法

1. 获取url串中的信息

返回值类型 方法名称 说明
String getAuthority() 获取此 URL 的授权部分。
Object getContent() 获取此 URL 的内容。
Object getContent(Class[] classes) 获取此 URL 的内容。
int getDefaultPort() 获取与此 URL 关联协议的默认端口号。
String getFile() 获取此 URL 的文件名。
String getHost() 获取此 URL 的主机名(如果适用)。
String getPath() 获取此 URL 的路径部分。
int getPort() 获取此 URL 的端口号。
String getProtocol() 获取此 URL 的协议名称。
String getQuery() 获取此 URL 的查询部分。
String getRef() 获取此 URL 的锚点(也称为“引用”)。

2. 获取url目标的资源

返回值类型 方法名称 说明
URLConnection openConnection() 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 获取url对象的Url连接器对象。将连接封装成了对象: java中内置的可以解析的具体协议的对象+socket。
InputStream openStream() 打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。也就是获取url资源的Socket的读取流,并舍弃应答头,只获取应答体。其底层是 openConnection().getInputStream()方法。
方法注释:

   URL类有一个特别的方法就是openConnection,返回一个URLConnection对象,此对象底层封装的是Socket流通,过URLConnection对象可以使用socket中的方法来获取网站的内容 ,URLConnection把浏览器的请求头和服务器返回的响应头都封装起来了,因此我们不用再向使用Socket一样手动写入浏览器请求头了。此URLConnection在应用层,而Socket在传输层。

URLConnection类

  1. 说明
       此类可以用来处理浏览器请求头等信息。内置一些常用的方法来获取请求头中的信息。

io

文章目录

  • 网络
  • 网络通信要素
      • 要素
          • 协议说明
  • Java之网络通信Socket
      • 说明
        • UDP的端点服务对象
        • TCP服务对象
          • 实例应用SocketTCP
  • Java之数据报包——DatagramPacket
      • 说明
      • 构造方法
  • Java之UDP的端点服务对象——DatagramSocket
      • 说明
      • 示例
    • 进阶应用之多线程
  • Java之TCP服务——Socket/ServerSocket
      • 说明
    • Socket
            • 构造方法说明
        • 其他重要提示
  • Java之Socket/ServerSocket为基础的C/S文件上传示例
    • 需求
      • 客户端源码
      • 服务端
  • Java之统一资源定位符——URL
      • 说明
            • URI、URL 和 URN
      • URL类中的常用方法
          • 方法注释:
      • URLConnection类
  • io
  • Java IO之键盘录入
  • Java File对象的常用方法
      • 判断方法
      • 获取文件信息方法
      • 文件列表与筛选
      • 文件操纵方法
  • File 之文件遍历与删除
      • 文件遍历
      • 文件删除
  • Java Properties类
      • 配置文件说明
      • Properties说明
        • 常用方法
            • Properties对象操作方法
            • 持久化存取方法
  • 实际使用过程中流对象的选择
        • 装饰设计模式扩展功能与继承模式扩展功能区别:
    • 流的操作规律:
    • 需求示例演示:
  • Print类详解
      • PrintStream:字节打印流说明
        • 注意
      • 常用函数
      • PrintWriter:字符打印流。
      • 自动刷新功能
  • SequenceInputStream 序列流
      • 说明
  • IO流之综合操作——文件的切割与合并
  • Java之序列流ObjectOutputStream/ObjectInputStream
      • 说明
      • 演示
  • Java之随机访问文件—RandomAccessFile
      • 说明
      • 构造函数参数
      • 常用函数说明
      • 其他
  • Java之管道流PipedInputStream/PipedOutputStream
      • 说明
      • 代码示例
  • Java之数据流DataInputStream/DataOutputStream
      • 说明
            • 注意:
  • Java之字节数组流ByteArrayInputStream/ByteArrayOutputStream
      • 说明
  • 反射
      • 获取一个类的Class对象方式
      • 获取不同类型的class
      • 类的加载
      • 根据一个类的Class对象获取其内部信息
      • 使用通过反射获得的数据
      • 使用反射获取注解
  • 内部类
      • 说明
      • 内部类与外部类成员变量的访问
      • 局部内部类
            • 局部内部类示例:
      • 匿名内部类
  • 枚举
      • 枚举类的使用
      • 如何定义枚举类
      • Enum类中的常用方法:
      • 使用enum关键字定义的枚举类实现接口的情况
      • 使用enum关键字定义枚举类
  • 按字节截取字符串

Java IO之键盘录入

// An highlighted block
io流是java中很重要的一个部分,不用扫描器进行键盘录入

InputStream in = System.in;  //其中System是系统类,成员in默认为键盘录入
try {
    int len = 0;
    StringBuilder sb = new StringBuilder(); //临时缓冲区
    while((len = in.read())!=-1){  //read为阻塞方法如果没有中断标记无法停止
        char ch = (char)len;
		//下面语段代码为BufferReader 的readLine中的实现方法
        if(ch == '\r'){
            continue;
        }
        if(ch == '\n'){
            System.out.println(sb);
            sb.delete(0, sb.length());
        }else{
       		sb.append(ch);
        }
        if((char)len=='0') {
            break;
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

/*有一种改进方法就是对InputStream 的对象进行装饰,其中会用到转换流进行字符流转字节流:
InputStreamReader  //字节流转字符流的桥梁
改进如下:
*/
public static void main(String[] args) {
        InputStream in = System.in;
        try {
            String str = null;
            StringBuilder sb = new StringBuilder();
            BufferedReader br = new BufferedReader(new InputStreamReader(in)); //不断进行装饰的过程
            while ((str = br.readLine())!=null){
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


Java File对象的常用方法

判断方法

函数名称 详细说明
canExecute() 是否是执行文件
canRead 是否允许读操作
canWrite() 是否允许读操作
exists() 目录或文件是否存在
isFile() 是否为文件
isDirectory() 是否为目录
isHidden() 是否为隐藏文件

以上方法返回值都为boolean类型


获取文件信息方法

返回值类型 函数名称 详细说明
String getName() 获取文件或目录名称
File getAbsoluteFile() 获取绝对路径的File对象形式,如果没有则为NULL
String getAbsolutePath() 返回绝对路径名字符串,如果没有则为NULL
String getParent() 返回父目录,如没有则返回 null。(构造器传入的父目录,不一定是绝对路径)
File getParentFile() 返回父目录的文件对象,如没有则返回 null
String getPath() 获取相对路径
long lastModified() 返回文件最后一次被修改的时间(毫秒值,需要Date转换)
long length() 返回文件的长度(字节数)
static File[] listRoots() Windows下返回列出所有盘符
long getFreeSpace() 返回指定盘符的剩余容量字节数

文件列表与筛选

返回值类型 函数名称 详细说明
String[] list() 列出当前目录下所有的文件夹和文件(包含隐藏文件)的列表,(类似于ls命令),如果调用list方法的file对象不是目录或是一个无法访问的系统目录则返回一个null,如果有目录,但目录下子目录为空,则返回一个长度为0的String数组
File[] listFiles() 返回指定目录中的文件和目录对象
String[] list(FilenameFilter filter) 返回目录中满足过滤器的文件和目录的名称。 (根据文件名称过滤)
File[] listFiles(FilenameFilter filter) 返回目录中满足指定过滤器的文件和目录的对象
File[] listFiles(FileFilter filter) 返回满足指定过滤器的目录中的文件和目录。

注释:
interface FilenameFilter接口的解释:
这里的文件过滤是用到了策略设计模式,详细用法我也不造车轮子了
引用链接:filenamefilter详细说明
这里我们会发现listFiles有重载形式:
filefilter和filenamefilter的区别


文件操纵方法

返回值类型 函数名称 详细说明
boolean createNewFile() 创建新文件,如果文件已存在不覆盖,返回false
boolean delete() 删除文件或目录,不可删除有内容的目录
boolean renameTo(File dest) 重命名文件,也可以剪切(与linux mv命令类似)传入的是想要更名的文件对象
boolean mkdir() 创建目录
boolean mkdirs() 递归的创建目录

未完待续…

File 之文件遍历与删除

文件遍历

 @Test
    public void test3() {
        File file = new File("E:\\");
        getDir(file, 0);
    }

    private void getDir(File file, int count) {
        File[] list = file.listFiles();
        //count 循环是来控制缩进
        for(int i = 0;i<count;i++){
            System.out.print("|--");
        }
        System.out.println("dir:"+file.getName());
        for (File file1 : list) {
            if (file1.isDirectory()) {
                getDir(file1, count + 1);
            } else {
                for(int i = 0;i<count;i++){
                    System.out.print("|--");
                }
                System.out.println("file::"+file1.getName());
            }
        }
    }

文件删除

public void test2() {
        File file = new File("E:\\ccc");
        getDir(file);
    }

    private void getDir(File file) {
        File[] list = file.listFiles();
        for (File file1 : list) {
            if (file1.isDirectory()) {
                getDir(file1);
            } else {
            	//递归删除文件
                System.out.println("file:"+file1.delete());
            }
        }
        //再删除目录
        System.out.println("dir:"+file.delete());
    }

Java Properties类

配置文件说明

  配置文件一般与记录用户对程序设置的永久化存储,每当你对手机电脑等系统或软件进行字体、主题等其他设置的选择都会保存在配置文件当中,每当程序启动加载进内存,程序一般都会先读取本地配置文件,加载用户之前的自定义配置。
  配置文件不仅用于持久化存储用户的一些配置,在Java中配合反射机制可以增强程序扩展性,这一点后面文章中在提及。

Properties说明

  官方文档说明:Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
  其实就是Properties是一个可以写到硬盘上的Map集合,默认的键和值都是String类型,适合操纵键值对方式存储的配置文件。

java.util class Properties 继承体系:
java.lang.Object
  |— java.util.Dictionary
    |—java.util.Hashtable
      |— java.util.Properties
从基础体系可以得知,Properties类可以使用Hashtable中继承过来的方法


常用方法

Properties对象操作方法
返回值 方法名称 说明
Object setProperty(String key, String value) 调用 Hashtable 的方法 put,将键值写入到HashTable集合中
void list(PrintWriter out) 将Properties对象的map集合用打印流输出(调试用)
String getProperty(String key) 获取指key的value
Set stringPropertyNames() 获取配置文件中的所有Key的Set集合

持久化存取方法
  1. 存储方法
返回值 方法名称 说明
void store(Writer writer, String comments) 将内存中的Map集合数据通过字符流写出
void store(OutputStream out, String comments) 将内存中的Map集合数据通过字节流写出
  • store方法为保存方法,第一个参数为保存的流对象,第二个参数为提示信息(不能有中文),在配置文件中是注释。
public void test1() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("Back color", "red");
        prop.setProperty("found size", "25");
        
        FileWriter fw = new FileWriter("E:\\b.txt");
        prop.list(System.out);//先显示到控制台上
        prop.store(fw,"Prompt information"); //再写入到b.txt文件中
    }
  1. 读取方法
返回值 方法名称 说明
void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)
void load(Reader reader) 按面向行的格式从输入字符流中读取属性列表(键和元素对)
  • 通过load方法 将文件中的键值对读取的内存中的HashTable中(也就是Properties对象中)

   /* 对已有的配置文件中的信息进行修改。 
    * 读取这个文件。
    * 并将这个文件中的键值数据存储到集合中。
    * 在通过集合对数据进行修改。
    * 在通过流将修改后的数据存储到文件中。
    */
   @Test
   public  void test() throws IOException{
       //读取这个文件。
       File file = new File("info.txt");
       FileReader fr = new FileReader(file);
       //创建集合存储配置信息。
       
       Properties prop = new Properties();
       prop.load(fr);
       prop.setProperty("wangwu", "16");
       
       // 一定要注意写入文件对象的位置, 如果在load方法之前创建FileWriter对象会覆盖原有文件的内容
       FileWriter fw = new FileWriter(file);
       prop.store(fw,"");
       fw.close();
       fr.close();
   }

实际使用过程中流对象的选择

  1. 输入流和输出流相对于内存设备而言.
    将外设中的数据读取到内存中:输入
    将内存的数写入到外设中:输出。

  2. 字符流的由来:
    其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。
    在对这个文字进行操作。简单说:字节流+编码表


字节流的两个顶层父类:
1,InputStream 2,OutputStream.

字符流的两个顶层父类:
1,Reader 2,Writer

这些体系的子类都以父类名作为后缀。
而且子类名的前缀就是该对象的功能。


字符流缓冲区附加功能:
BufferedWriter
:newLine();
BufferedReader:
: readLine();

装饰设计模式扩展功能与继承模式扩展功能区别:

  对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。 装饰和继承都能实现一样的特点:进行功能的扩展增强。

有什么区别呢?
首先有一个继承体系。
Writer
|–TextWriter:用于操作文本
|–MediaWriter:用于操作媒体。

想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。

Writer
|–TextWriter:用于操作文本
|–BufferTextWriter:加入了缓冲技术的操作文本的对象。
|–MediaWriter:用于操作媒体。
|–BufferMediaWriter:

到这里就哦了。但是这样做好像并不理想。
如果这个体系进行功能扩展,有多了流对象。
那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,
导致继承体系越来越臃肿。不够灵活。

重新思考这个问题?
既然加入的都是同一种技术–缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。

class Buffer{
	Buffer(TextWriter w)
	{}
	
	Buffer(MediaWirter w)
	{
	
	}
}
class BufferWriter extends Writer{
	BufferWriter(Writer w)
	{
	}
}

Writer
|–TextWriter:用于操作文本
|–MediaWriter:用于操作媒体。
|–BufferWriter:用于提高效率。

装饰比继承灵活。

特点:装饰类和被装饰类都必须所属同一个接口或者父类。


字节流:
InputStream
OutputStream

字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
字符流:
Writer Reader
FileReader
FileWriter
BufferedReader
BufferedWriter


转换流:
InputStreamReader :字节到字符的桥梁。解码。
OutputStreamWriter:字符到字节的桥梁。编码。

流的操作规律:

之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。

想要知道开发时用到哪些对象。只要通过四个明确即可。

1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer

2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream

到这里,就可以明确需求中具体要使用哪个体系。

3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流

目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流

4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。


需求示例演示:

需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer

3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File

FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter(“b.txt”);

4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
BufferedWriter bufw = new BufferedWriter(new FileWriter(“b.txt”));

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

需求2:读取键盘录入信息,并写入到一个文件中。

1,明确源和目的。
	源:InputStream Reader
	目的:OutputStream  Writer
2,是否是纯文本呢?
	是,
	源:Reader
	目的:Writer
3,明确设备
	源:
		键盘。System.in
	目的:
		硬盘。File
		
	InputStream in = System.in;
	FileWriter fw = new FileWriter("b.txt");
	将读取的字节数据转成字符串。再由字符流操作。这样做可以完成,但是麻烦。
4,需要额外功能吗?
	需要。转换。	将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
		所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
	InputStreamReader isr = new InputStreamReader(System.in);
	FileWriter fw = new FileWriter("b.txt");
	
	还需要功能吗?
	需要:想高效。
	BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
	BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader(“a.txt”);
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader(“a.txt”);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader(“a.txt”));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));


需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out

	InputStream in = System.in;
	OutputStream out = System.out;
	
4,明确额外功能?
	需要转换,因为都是字节流,但是操作的却是文本数据。
	所以使用字符流操作起来更为便捷。
	InputStreamReader isr = new InputStreamReader(System.in);
	OutputStreamWriter osw = new OutputStreamWriter(System.out);
	
	为了将其高效。
	BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
	BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.

1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File 
FileWriter fw = new FileWriter("a.txt");
fw.write("你好"); 

注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);

需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

什么时候使用转换流呢?

1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
	提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。

Print类详解

PrintStream:字节打印流说明

  1. 提供了打印方法可以对多种数据类型值进行打印。并保持数据的表示形式。
    注意:这里说的打印方法是print, 此方法可以直接打印数据的原有形式不做编码转换,也就是下面介绍的print方法.
  • 也就是说想要保存数据原样性,推荐使用此类。
  1. 它不抛IOException.

  2. 构造函数接收三种类型的值:
    字符串路径。
    File对象。
    字节输出流。

注意

   PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类


常用函数

返回值 方法名 说明
void write(int b) 将指定的字节转换成默认字符集的字符写出, 就是把int转换成char再写出。(int 类型是4个字节,而Write方法只写入int的最低八位,前面的3个字节完全忽略)
void println(int x) 把所有传入此方法的内容转换成字符串原样打印出去 (此方法参数有多种重载形式,这里只拿int来演示)

PrintWriter:字符打印流。

  1. 构造函数参数:
    字符串路径。
    File对象。
    字节输出流。
    字符输出流。

自动刷新功能

   自动刷新功能,PrintWriter、PrintStream都支持,下面用PrintWriter的其中一个构造函数来解释说明。

构造方法名 说明
PrintWriter(OutputStream / Writer out, boolean autoFlush)   通过现有的 OutputStream 创建新的 PrintWriter。autoFlush为true是自动刷新(不用手动调用flash),只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。如果启用了自动刷新,则这些方法使用平台自有的行分隔符概念,而不是换行符。

SequenceInputStream 序列流

说明

  与打印流不同的是,序列流只负责源的操纵,SequenceInputStream继承于InputStream,其作用是为多个源流的合并为一个大流。


构造方法 说明
SequenceInputStream(Enumeration e) 把集合Vector中存储的流合并成一个大流,
 public static void main(String[] args) throws IOException {
        Vector<InputStream> v = new Vector<>();
        
        v.add(new FileInputStream("E:\\IO测试文件夹\\a.txt"));
        v.add(new FileInputStream("E:\\IO测试文件夹\\b.txt"));
        
        BufferedInputStream bis = new BufferedInputStream(
        				new SequenceInputStream(v.elements()));

        PrintStream ps = new PrintStream("E:\\IO测试文件夹\\E.txt");
        int len = 0;
        while ((len = bis.read())!=-1){
            ps.write(len);
        }
        
        ps.close();
        bis.close();
    }

  但是Vector效率不如ArrayList高效,但是SequenceInputStream(Enumeration e) 接收的是枚举类型,ArrayList只有迭代器。因此我们需要集合中的工具类Collections中的一个函数:

返回值 函数名称 说明
static Enumeration enumeration(Collection c) 传入一个Collection集合,返回枚举

  • 改进如下:
public static void main(String[] args) throws IOException {
        List<FileInputStream> list = new ArrayList<>();
        list.add(new FileInputStream("E:\\IO测试文件夹\\a.txt"));
        list.add(new FileInputStream("E:\\IO测试文件夹\\E.txt"));

        //iterator 转 enumeration
        Enumeration<FileInputStream> enumeration = Collections.enumeration(list);


        BufferedInputStream bis = new BufferedInputStream(
                new SequenceInputStream(enumeration));

        FileOutputStream ps = new FileOutputStream("E:\\IO测试文件夹\\b.txt");
        int len = 0;
        while ((len = bis.read())!=-1){
            ps.write(len);
        }
        ps.close();
        bis.close();
    }

IO流之综合操作——文件的切割与合并

import org.junit.Test;
import java.io.*;
import java.util.*;

public class 文件切割 {

    private int FileMegabit = 1;

    @Test
    public void test1() throws IOException {

        String srcDir = "F:\\JavaSourceCode\\TestModule\\files\\";
        String srcFileName = "file.rar";
        String outDri = "F:\\JavaSourceCode\\TestModule\\files\\切割目录\\";

        //cutfile(srcDir, srcFileName, outDri);

        //合并
        merge(outDri, outDri);
    }

    private void merge(String mergePath, String conDir) throws IOException {

        Properties p = new Properties();

        p.load(new FileReader(conDir + "partConf.ini"));
        int count = Integer.parseInt(p.getProperty("count"));
        String fileName = p.getProperty("fileName");


        File srcDirFile = new File(mergePath);
        if (!srcDirFile.isDirectory()) {
            throw new RuntimeException("请选择包含碎片文件的目录");
        }

        File[] files = srcDirFile.listFiles((fil) -> fil.getName().endsWith(".part"));
        if (files.length != count ) {
            throw new RuntimeException("碎片文件不完整,无法合并");
        }


        ArrayList<FileInputStream> sofList = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            sofList.add(new FileInputStream(new File(mergePath, fileName + i + ".part")));
        }
        Enumeration<FileInputStream> enumeration = Collections.enumeration(sofList);
        SequenceInputStream sis = new SequenceInputStream(enumeration);

        FileOutputStream fos = new FileOutputStream(mergePath + fileName);

        byte[] flush = new byte[1024 * 1024];
        int len = -1;
        while ((len = sis.read(flush)) != -1) {
            fos.write(flush, 0, len);
        }
        fos.close();
    }

    public void cutfile(String srcDir, String srcFileName, String outDri) throws IOException {
        File srcFile = new File(srcDir, srcFileName);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));

        byte[] b = new byte[1024 * 1024 * FileMegabit];
        int len = 0;

        int fileCuttingCounter = 0;

        while ((len = bis.read(b)) != -1) {
            FileOutputStream fos = new FileOutputStream(outDri + srcFileName + fileCuttingCounter + ".part");
            fos.write(b, 0, len);
            fileCuttingCounter++;
            fos.close();
        }
        bis.close();
        cutInfoStorage(fileCuttingCounter, srcFileName, outDri);
    }

    private void cutInfoStorage(int count, String fileName, String saveDir) throws IOException {
        Properties p = new Properties();
        p.setProperty("count", String.valueOf(count));
        p.setProperty("fileName", fileName);
        p.store(new FileWriter(saveDir + "partConf.ini"), 
        					"cutting file information Not delete");
    }
}

Java之序列流ObjectOutputStream/ObjectInputStream

说明

  对象序列化就是把堆内存中的对象持久化处理(保存到硬盘上)。
  对象序列化前提:对象想要被序列化,就必须实现Serializable接口,此接口是一个标记接口,没有实现方法。


  1. vransient关键字介绍:可以让阻止非静态数据(堆内存)的数据序列化。

  2. Serializable接口介绍:实现此接口的类都有一个隐示的serialVersionUID (这个UID也可以手动定义)其定义格式为:ANY-ACCESS-MODIFIER static final long serialVersionUID。这个UID用来判断对象和类是否处于同一个版本,如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException
       官方文档上说,强烈建议自定义UID,因为编译器版本的不同,可能对同一个类进行序列化后导致ID不一致。

其他提示:静态成员不能被序列化


演示

/**
 * 自定义一个对象,用于测试对象序列化
 */
class Person implements Serializable {

    public static final long serialVersionUID = 42L;  //自定义序列化标号ID
    //如果不写,系统会根据类中的内容计算出一个序列化ID
    //加载时根据ID判断硬盘中的这个对象是否属于这个类。

    private String name;
    transient int age;  //transition 关键字:阻止堆内存中的数据序列化,数据不能写入硬盘,
                        //只能在堆内存加载
    static String country = "cn";   //静态不能被序列化

    Person(String name, int age, String country) {
        this.name = name;
        this.age = age;
        Person.country = country;
    }

    @Override
    public String toString() {
        return name + ":" + age + ":" + country;
    }
}

对象序列化(存储)演示

	//把一个对象写入到硬盘。
    public static void writeObj() throws IOException {
        //创建写对象流,和保存位置
        ObjectOutputStream oos =
                new ObjectOutputStream(new FileOutputStream("obj.object"));

        //把一个对象写入到硬盘中,如果有多次写入,则顺序存储
        oos.writeObject(new Person("lisi0", 399, "kr"));
        //oos.writeObject(new Person("lisi0", 399, "kr"));
        oos.close();
    }

对象反序列化(读取)演示
  读取的readObject方法会抛出一个ClassNotFoundException (类找不到异常),因为要读取对象文件有一个前提,你必须有这个对象的.class文件。如果你有这个对象文件而没有类文件,这个对象就无法在内存中创建。
  readObject方法是按照顺序进行读取的,也就是说,调用一次readObject就读一次下一个对象。
还有一点就是要注意,readObject()方法好像没有结束标记,读到末尾会抛出EOFException,因此在catch中结束就好了,有知道的小伙伴可以留言评论一下。

	//读取硬盘的对象
    public static void readObj() throws Exception {
      ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("files\\obj.object"));

        Person person1 = (Person) ois.readObject();
        System.out.println(person1.toString());

        Person person2 = (Person) ois.readObject();
        System.out.println(person2.toString());
    }

Java之随机访问文件—RandomAccessFile

说明

  RandomAccessFile不是io体系中的四大类的子类。它直接继承与Object类。用此类存储的文件字节数必须有规律,因为他使用的是下标进行读取的,有数组的特性。

  • 总体来说就是通过seek方法可以对文件的任意位置进行读写。

其特点为:
1,该对象即能读,又能写。
2,该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素,
3,可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。
4,其实该对象就是将字节输入流和输出流进行了封装。
5,该对象的源或者目的只能是文件。通过构造函数就可以看出。
6,如果文件不存在,则创建,如果文件存在,则不覆盖文件。(注意:这里的不覆盖指的是文件的覆盖,与下面所说的内容覆盖不同)


构造函数参数

  • mode 参数指定用以打开文件的访问模式。允许的值及其含意为:
含意
“r” 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
“rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
“rws” 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
“rwd” 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。

常用函数说明

  • 此类提供了大量的各种形式的读写方法,由于方法太多这里就不一一赘述了,详情参看官方文档。

其他

   我们可以通过seek(long pos) 方法 设置指针的偏移位置,在通过其他的read方法去取数据,从而向数组一样读取文件的任意位置。
   由于是通过指针进行操作的,因此如果用新的RandomAccessFile对已有的文件进行writer操作时不会记录原有指针的位置,也就是说指针从0开始写入,这样会覆盖原字节数组下标的数据。
   由于他有随机访问的特性,因此可以用于多线程下载。

Java之管道流PipedInputStream/PipedOutputStream

说明

  管道流应结合多线程使用。因为官方文档上说对这两个对象使用单个线程,可能会造成该线程死锁。

代码示例

import java.io.*;
/**
 * 管道流Piped:
 * 说明:将输入流和输出流连接起来,(之前的方法是在内存中定义数组临时存储两个流之间的变量)
 * 注意:使用管道流,读和写不能在同一个线程执行,会出现死锁。
 *
 */

//read线程类
class Read implements Runnable {
    private PipedInputStream in;

    Read(PipedInputStream in) {
        this.in = in;
    }

    public void run() {
        try {
            byte[] buf = new byte[1024];

            System.out.println("读取前。。没有数据阻塞");
            int len = in.read(buf); //假如读线程先获得cpu执行权,如果没有数据,
                                    // 线程就会进入挂起(阻塞/暂停)状态,等待写入完成
            System.out.println("读到数据。。阻塞结束");

            String s = new String(buf, 0, len);

            System.out.println(s);

            in.close();

        } catch (IOException e) {
            throw new RuntimeException("管道读取流失败");
        }
    }
}
//write线程类
class Write implements Runnable {
    private PipedOutputStream out;
    Write(PipedOutputStream out) {
        this.out = out;
    }
    public void run() {
        try {
            System.out.println("开始写入数据,等待3秒后。");
            Thread.sleep(3000);
            out.write("piped lai la".getBytes());
            out.close();
        } catch (Exception e) {
            throw new RuntimeException("管道输出流失败");
        }
    }
}

public class PipedStreamTest {
    public static void main(String[] args) throws IOException {
        PipedInputStream in = new PipedInputStream();
        PipedOutputStream out = new PipedOutputStream();
        in.connect(out); //把连个管道进行连接

        Read r = new Read(in);
        Write w = new Write(out);
        new Thread(r).start();
        new Thread(w).start();
    }
}

Java之数据流DataInputStream/DataOutputStream

说明

  1. 此流用于操作基本数据类型,数据流是FilterOutputStream的子类。此类是一个装饰类,它不能直接操作资源。需要从构造函数中传递资源对象。
      构造方法:DataOutputStream(OutputStream out) 创建一个新的数据输出流,将数据写入指定基础输出流。
  2. 数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。

注意:

  查看官方文档的方法摘要就会发现。很多方法在管道流和RandomAccessFile等其他流中见到过,但是注意,管道流是操作管道的,RandomAccessFile是用于随机访问,虽然功能类似,但操作的对象和不一样,而数据流是专门保存原始数据的。
  如想要向文件写入一个int类型的数据,普通流的write只写入最低8位,而这个类中的方法提供了原样写入基本数据类型的功能。

Java之字节数组流ByteArrayInputStream/ByteArrayOutputStream

说明

  此流用于操作内存。由于此对象没有调用系统底层资源,因此此流不用关闭资源,且不会产生任何 IOException。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ByteArrayStreamDemo {
	public static void main(String[] args) {
		ByteArrayInputStream bis = new ByteArrayInputStream("abcedf".getBytes());
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int ch = 0;
		while((ch=bis.read())!=-1){
			bos.write(ch);
		}
		System.out.println(bos.toString());
	}
}

其他详情请参考官方文档

反射

获取一个类的Class对象方式

public void getClassTest() {
    //方式1
    Class<TestPerson> aClass1 = TestPerson.class;

    //方式2
    TestPerson testPerson = new TestPerson();
    Class<? extends TestPerson> aClass = testPerson.getClass();

    //方式3
    Class<?> aClass2 = null;
    try {
        aClass2 = Class.forName("getClass.TestPerson");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    //如下的hashcode相等1018547642 说明
    //一个类在内存中只有一个Class对象
    //一个类被加载后,类的整体个结构都会被封装在Class对象中。
    System.out.println(aClass.hashCode());
    System.out.println(aClass1.hashCode());
    if (aClass2 != null) {
        System.out.println(aClass2.hashCode());
    }

    //方式4:基本内置类型的包装类都有一个Type属性
    Class<Integer> aClass3 = Integer.TYPE;

    //方式5:根据一个类的Class对象,获得其父类对象
    Class<? super TestPerson> aClass4 = aClass1.getSuperclass();
}

获取不同类型的class

 public void test3() {
    Class<int[]> aClass = int[].class;
    Class<Override> overrideClass = Override.class;
    Class<Integer> integerClass = int.class;
    //注意void
    Class<Void> voidClass = void.class;
    Class<Class> classClass = Class.class;
    Class<String[][]> aClass1 = String[][].class;
    Class<ElementType> elementTypeClass = ElementType.class;
    Class<Integer> integerClass1 = Integer.class;
    Class<Comparable> comparableClass = Comparable.class;


    int[] int10 = new int[10];
    int[] int100 = new int[100];
    //不同长度,同一类型的数组Class对象也是唯一的
    System.out.println(int10.getClass().hashCode());
    System.out.println(int100.getClass().hashCode());

    //同一类型不同维度,hashCode不行的,Class对象不是同一
    int[][] ints = new int[10][10];
    System.out.println(ints.getClass().hashCode());
}

类的加载

  1. 类对象的创建过程,加载-》链接-》初始化
    1).加载到内存,会产生一个Class对象
    2).执行链接 结束后静态变量会初始化为0
    3).初始化,如果有父类,则执行父类会从1)开始执行;jvm会执行clinit方法把静态修饰的内容提取出来进行初始化赋值

  2. 类的初始化注意点:
    通过子类类名点父类静态不会初始化子类,只会初始化父类
    对象数组的定义不会初始化对象
    通过类名点public static final修饰的内容(常量池的内容)不会触发这个类和其父类的初始化

    //获取类加载器可以加载的路径,只有class文件在这些路径下,才会被jvm加载
    @Test
    public void getClassPath() {
        String paths = System.getProperty("java.class.path").toString();
        String[] split = paths.split(";");
        for (String s : split) {
            System.out.println(s);
        }
    }

根据一个类的Class对象获取其内部信息

  1. 获取类的名称
Class c1 = Class.forName("getClass.TestPerson");
String name = c1.getName();//获取全路径
String simpleName = c1.getSimpleName();//只获取类名称

2.获取成员属性

//获取所有属性 不加Declared,不能获取私有修饰的属性
Field age = c1.getField("age");
//获取指定属性
Field privateName = c1.getDeclaredField("name");
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}
//结果
//private java.lang.String getClass.TestPerson.name
//public int getClass.TestPerson.age
  1. 获取一个类的方法对象
//此方法会获取本类和父类所有public方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
    System.out.println(method);
}

//只获取本类所有方法包括private
methods = c1.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method);
}

//获取指定的方法 参数格式 (方法名称,参数类型的Class对象)如果空参就省略。
Method getName = c1.getMethod("getName");
System.out.println(getName);

//传参数的Class对象是用来区分重载方法
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);

4.获取类的构造器

//获本类全部取构造器
Constructor[] constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}
//获取指定构造器
Constructor constructor = c1.getConstructor();
System.out.println(constructor);
//根据参数获取构造器
Constructor constructor1 = c1.getConstructor(String.class, int.class);
System.out.println(constructor1);

使用通过反射获得的数据

  1. 创建这个类的对象
Class c1 = Class.forName("getClass.TestPerson");

// newInstance已经过时,他调用无参数构造器,如果类中没声明无参构造,或权限不足,那他将抛出异常
TestPerson person = (TestPerson) c1.newInstance();

//推荐 他更显性化显示具体用哪个构造器来创建的对象
person = (TestPerson)c1.getConstructor().newInstance();
person.setAge(18);
int age = person.getAge();
//获取有参构造,并创建对象
person = (TestPerson) c1.getConstructor(String.class, int.class).newInstance("aaa", 22);
System.out.println(person);//TestPersion{name='aaa', age=22}
  1. 通过反射调用方法

person = (TestPerson)c1.getConstructor().newInstance();
Method setName = c1.getMethod("setName", String.class);
//注意:虽然获取到了方法,方法的调用需要传入一个对象,绑定这个方法,然后用传入的对象调用方法
Object aaa = setName.invoke(person, "bbb");
System.out.println(person.getName());
  1. 通过反射操作属性
//  注意:私有修饰需要使用Declared
Field name = c1.getDeclaredField("name");
//私有属性需要设置访问权限为true
name.setAccessible(true);//这是一个安全检查的开关,设置为true可提高运行效率
name.set(person,"ccc");
System.out.println(person.getName());

使用反射获取注解

@Test
    public void refAnnotation() throws NoSuchFieldException {
        Class c1 = AnnotationTestClass.class;

		//获取一个类上的注解
        //注意此处强转,以及参数为注解名称的Class对象
        DbTable dbTable = (DbTable)c1.getAnnotation(DbTable.class);
        String value = dbTable.value();
        System.out.println(value);

		//获取一个域上的注解
        //先获取指定的属性
        Field name = c1.getDeclaredField("name");
        //通过属性对象获取他的注解
        DbField field = name.getAnnotation(DbField.class);
        //通过注解获取其参数值
        String column = field.column();
        String type = field.type();
        int length = field.length();
        System.out.println(column+" "+type+" "+length);
    }
  • 被反射的测试类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@DbTable("db_Table")
public class AnnotationTestClass {
    @DbField(column = "id",type = "int",length = 10)
    private int id;

    @DbField(column = "name",type = "varchar",length = 20)
    private String name;

    public AnnotationTestClass() {
    }

    public AnnotationTestClass(int id, String name) {
        this.id = id;
        this.name = name;
    }
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DbTable{
    String value();
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DbField{
    String column();

    String type();

    int length();
}
  • 自定义的注解
    注意:声明周期权限应设置为RetentionPolicy.RUNTIME否则反射无法获取注解信息
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DbTable{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DbField{
    String column();

    String type();

    int length();
}

内部类

说明

  1. 定义在一个类内部的类,内部类被定义在外部类的成员位置,因此可以被成员修饰符修饰(普通类是不允许的)。
  2. 什么时候定义内部类呢?
    (1) 一个类需要直接访问另一个类的成员,普通方式需要在这个类中创建被访问成员的类的对象才能使用,因此简化操作,可以把这类放到被访问成员的类内。
    (2)一般用于类的设计。分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容。这时就是还有的事物定义成内部类来描述。如有一个Person类,其内部还有个Heart类
  3. 直接访问外部类中的内部类中的成员: OutClass.InnerClass oc = new OutClass().new InnerClass();
  4. 如果内部类是静态的, 其访问的外部类成员也必须是静态的。调用格式相当于一个外部类: Outer.Inner in = new Outer.Inner();
  5. 如果内部类是静态的,成员是静态的: Outer.Inner.function();
  6. 非静态内部类不能有静态成员。

(1)示例

public class OutClass{
    private int num = 10;
    class InnerClass{
        public void print(){
        	//直接访问外部类成员
            System.out.println(num);
        }
    }

    @Test
    public void test(){
        new InnerClass().print();
    }
}
  1. 内部类访问特点:
    1,内部类可以直接访问外部类中的成员。
    2,外部类要访问内部类,必须建立内部类的对象。

内部类与外部类成员变量的访问

class Outer{
	int num = 3;
	class Inner{
		int num = 4;
		void show(){
			int num = 5;
			System.out.println(num);//访问5
			//由于不明确this是属于哪个类的,因此需要加上类名.this
			System.out.println(Outer.this.num);//访问3
			System.out.println(Inner.this.num);//访问4
		}
	}
	void method(){
		new Inner().show();
	}
}
class InnerClassDemo2 {
	public static void main(String[] args) {
		new Outer().method();
	}
}
  • 为什么内部类能直接访问外部类中成员呢?那是因为内部类持有了外部类的引用。 外部类名.this。

局部内部类

  1. 内部类可以存放在局部位置上。 如方法体,循环等代码块中。
  2. 内部类在局部位置上只能访问局部中被final修饰的局部变量。必须被final修饰的原因:因为方法在栈内存,而对象在堆内存,如果方法内部有非final的局部变量会因方法的结束而销毁,而内部类的对象可能还会存在,因此内部类访问一个不存在的变量会出现问题。
  3. 局部内部类不能把权限修饰符和static修饰
局部内部类示例:
class Outer {
    int num = 3;
    void method(final int y) {
        //局部内部类要想访问局部变量,此变量必须是final修饰
        final int x = 9;
        class Inner {
            @Override
            public String print() {
                return "show ..." + x + y;
            }
        }
        Object in = new Inner();
        in.toString();
    }
}

匿名内部类

  1. 前提
    匿名内部类。就是内部类的简写格式。
      必须有前提:
      内部类必须继承或者实现一个外部类或者接口。
    匿名内部类:其实就是一个匿名子类对象。
    格式:new 父类/接口(){子类内容}
  2. 注意:匿名内部类的实现方法必须是父类/接口重写的方法。否则即使你有自己特有的方法,通过父类/接口也无法调用。

枚举

枚举类的使用

1.枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类(类的对象有限个,且内容确定)
2.当需要定义一组常量时,强烈建议使用枚举类
3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式。


如何定义枚举类

方式一:jdk5.0之前,自定义枚举类
方式二:jdk5.0,可以使用enum关键字定义枚举类


Enum类中的常用方法:

values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
toString():返回当前枚举类对象常量的名称


使用enum关键字定义的枚举类实现接口的情况

情况一:实现接口,在enum类中实现抽象方法
情况二:让枚举类的对象分别实现接口中的抽象方法


public class SeasonTest {

    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);

    }

}
//自定义枚举类
class Season{
    //1.声明Season对象的属性:private final修饰
    private final String seasonName;
    private final String seasonDesc;

    //2.私有化类的构造器,并给对象属性赋值
    private Season(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //3.提供当前枚举类的多个对象:public static final的
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","夏日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高气爽");
    public static final Season WINTER = new Season("冬天","冰天雪地");

    //4.其他诉求1:获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }
    //4.其他诉求1:提供toString()
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

使用enum关键字定义枚举类

说明:定义的枚举类默认继承于java.lang.Enum类

public class SeasonTest1 {
    public static void main(String[] args) {
        Season1 summer = Season1.SUMMER;
        //toString():返回枚举类对象的名称
        System.out.println(summer.toString());

//        System.out.println(Season1.class.getSuperclass());
        System.out.println("****************");
        //values():返回所有的枚举类对象构成的数组
        Season1[] values = Season1.values();
        for(int i = 0;i < values.length;i++){
            System.out.println(values[i]);
            values[i].show();
        }
        System.out.println("****************");
        Thread.State[] values1 = Thread.State.values();
        for (int i = 0; i < values1.length; i++) {
            System.out.println(values1[i]);
        }

        //valueOf(String objName):返回枚举类中对象名是objName的对象。
        Season1 winter = Season1.valueOf("WINTER");
        //如果没有objName的枚举类对象,则抛异常:IllegalArgumentException
//        Season1 winter = Season1.valueOf("WINTER1");
        System.out.println(winter);
        winter.show();
    }
}

interface Info{
    void show();
}

//使用enum关键字枚举类
enum Season1 implements Info{
    //1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
    SPRING("春天","春暖花开"){
        @Override
        public void show() {
            System.out.println("春天在哪里?");
        }
    },
    SUMMER("夏天","夏日炎炎"){
        @Override
        public void show() {
            System.out.println("宁夏");
        }
    },
    AUTUMN("秋天","秋高气爽"){
        @Override
        public void show() {
            System.out.println("秋天不回来");
        }
    },
    WINTER("冬天","冰天雪地"){
        @Override
        public void show() {
            System.out.println("大约在冬季");
        }
    };

    //2.声明Season对象的属性:private final修饰
    private final String seasonName;
    private final String seasonDesc;

    //2.私有化类的构造器,并给对象属性赋值

    private Season1(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //4.其他诉求1:获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }
//    //4.其他诉求1:提供toString()
//
//    @Override
//    public String toString() {
//        return "Season1{" +
//                "seasonName='" + seasonName + '\'' +
//                ", seasonDesc='" + seasonDesc + '\'' +
//                '}';
//    }


//    @Override
//    public void show() {
//        System.out.println("这是一个季节");
//    }
}

按字节截取字符串

   在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。但对应的字节数不同,一个汉字占两个字节。定义一个方法,按照最大的字节数来取子串。如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”。


根据汉字起始字节都是负数,因此可以判断负数个数的奇偶进行取舍操作。

import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        String str = "ab你好cd谢谢";
//		str = "ab琲琲cd琲琲";

//		int len = str.getBytes("gbk").length;		
//		for(int x=0; x
//			System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1));
//		}

        int len = str.getBytes("utf-8").length;
        for(int x=0; x<len; x++){
            System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByU8Byte(str, x+1));
        }
        
//		String str = "琲";
//		byte[] buf = str.getBytes("gbk");
//		for(byte b : buf){
//			System.out.println(b);//-84  105 
//		}
    }

    public static String cutStringByU8Byte(String str, int len) throws IOException {
        byte[] buf = str.getBytes("utf-8");
        int count = 0;
        for(int x=len-1; x>=0; x--){
            if(buf[x]<0) { 
                count++;
            } else {
                break;
            }
        }

        if(count%3==0) {
            return new String(buf,0,len,"utf-8");
        } else if(count%3==1) {
            return new String(buf,0,len-1,"utf-8");
        } else {
            return new String(buf,0,len-2,"utf-8");
        }
    }

    public static String cutStringByByte(String str,int len) throws IOException{
        byte[] buf = str.getBytes("gbk");
        int count = 0;
        for(int x=len-1; x>=0; x--){
            if(buf[x]<0) {
                count++;
            } else {
                break;
            }
        }
        if(count%2==0) {
            return new String(buf,0,len,"gbk");
        } else {
            return new String(buf,0,len-1,"gbk");
        }
    }
}

你可能感兴趣的:(Java基础)