学习总结(2022.04.12-2022.04.20)
IO的分类
按照数据流向分(以内存为参照物):
- 输入:外设->内存
- 输出:内存->外设
按照数据类型分:
- 字节流(1B = 0000 0000)
- 字符流
字节流一般用于非文本文件;字符流一般用于文本文件。
为什么会有字符流?因为用字节流读取非英文和数字的数据可能会发生错误
4个抽象基类及其子类
| 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 | |
|---|---|---|---|---|
| 抽象父类 | OutputStream<br>void write(byte[] b) |
InputStream<br/>int read(byte[] b) |
Writer<br>void write(char[] c)<br/>void writer(String s) |
Reader<br>int read(char[] c) |
| 文件 | FileOutputStream(File file, boolean append) |
FileInputStream(File file) |
FileWriter(File file, boolean append) |
FileReader(File file) |
| 缓冲 | BufferedOutputStream(OutputSteam out, int size) |
BufferedInputStream(InputStream in, int size) |
BufferedWriter(Writer out, int size) |
BufferedReader(Reader in, int size)<br>String readLine() |
| 转换(字节转字符) | OutputStreamWriter(OutputStream out, String charsetName) |
InputStreamReader(InputStream in, String charsetName) |
||
| 数据(输入/输出java基本类型) | DataOutputStream(OutputStream out)<br>void writeInt(int a)... |
DataInputStream(InputStream in)<br>int readInt()... |
||
| 打印(输入/输出字符串) | PrintStream(OutputStream out) <br>int print(int a)... |
PrintWriter(Writer out)<br>int print(int a) |
||
| 对象(序列化/反序列化) | ObjectOutputStream(OutputStream out)<br>void writeObject(Object obj) |
ObjectInputStream(InputStream in)<br>Object readObject() |
换行方式
“\r\n”
System.lineSeparator()
标准IO流
System.in本质是InputStream
System.out本质是PrintStream
进程/线程 同步/异步
进程是操作系统调度的基本单位,线程是cpu调度的基本单位
同步是调用可以立即得到结果,但是需要等待;异步是调用不能立即得到结果,但是可以不等待
java程序运行原理
jvm是多线程的,每次运行至少有两个线程:main线程和GC线程
java是抢占式线程调度,但是java自带的setPriority()方法并不能改变线程的优先级,因为java中的优先级是静态的,只能给操作系统一个建议,实际上操作系统有自己的一套线程优先级
多线程的实现方式
- 定义一个类继承Thread类,重写run()方法,创建该类对象,通过start()创建线程(如果使用run()只是普通的调用子类方法,并不能达到创建多线程的效果)特点:单继承
- 定义一个类实现Runnable接口,重写run()方法,创建Runnable子类对象,再创建Thread对象,并将该子类对象作为参数传递。特点:实现接口
- 定义一个类实现Callable接口,重写call()方法,创建FutureTask对象并将该类的对象作为参数传递,再创建Thread对象将FutureTask对象作为参数传递。特点:有返回值
线程API
1 | |
安全中断线程
线程执行完毕就会中断。但是很多时候我们需要自己控制线程终止,然而API中的stop()方法存在线程不安全问题,所以一般定义一个成员变量boolean flag来控制。
synchronized关键字
基本语法
1 | |
锁对象可以是:
- 任意一个Object及其子类对象,java中的任意对象都可以当锁,都存在wait()、notify()和notifyAll()方法。
- this关键字,即是令包含该代码块的类对象当锁。
- 字节码文件对象(类名.class)
Lock类
成员方法:
1 | |
lock是一把真正的锁类,可以让我们看到加锁解锁的过程。
死锁的原因及解决方法
原因:一般出现在同步代码块嵌套,因为2个或以上的线程抢夺资源而造成互相等待。
1 | |
解决方式:
- 更改加锁顺序
1 | |
- 再加一把锁,将非原子操作改为原子操作
1 | |
线程池
线程池有三种子类:
ExecutorService newCachedThreadPool():根据需要创建新线程,也可以自动删除,60s处于空闲状态的线程。线程数量可变。ExecutorService newFixedThreadPool(int nThreads):线程数量固定,维护一个无界队列(暂存已提交的来不及执行的任务),按照任务的提交顺序,将任务执行完毕。ExecutorService newSingleThreadExecutor():单个线程,其他特点如上。
两种成员方法:
1 | |
定时任务与定时器
Timer定时器是一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
两种构造方法:
1 | |
四种成员方法:
1 | |
UDP/TCP
UDP发送端步骤:
DatagramSocket datagramSocket = new DatagramSocket(int port);创建发送端的Socket对象DatagramPacket sendPacket= newDatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port);创建用于发送的数据报包datagramSocket.send(sendPacket);把包发送出去datagramSocket.close();关闭Socket
UDP接收端步骤:
DatagramSocket datagramSocket = new DatagramSocket(int port);创建接收端的Socket对象DatagramPacket receivePacket = new DatagramPacket(byte[] buf, int offset, int length);创建用于接收的数据报包datagramSocket.receive(receivePacket);receive方法进行接收byte[] data = receivePacket.getData();解析数据datagramSocket.close();关闭Socket
UDP可以通过多线程优化,达到两端互相通信的效果
TCP客户端步骤:
Socket socket = new Socket(String host, int port);创建客户端的socket对象OutputStream out = socket.getOutputStream();从socket中获取输入/输出流out.write(byte[] b);利用输入/输出流进行读写操作socket.close();关闭Socket
TCP服务端步骤:
ServerSocket serverSocket = new ServerSocket(int port);创建服务端的ServerSocket 对象Socket socket = serverSocket.accept();利用accept方法建立连接,得到socket对象InputStream in = socket.getInputStream();从socket中获取输入/输出流in.read(byte[] b);利用输入/输出流进行读写操作socket.close();关闭Socket
TCP可以通过ObjectInputStream/ObjectOutputStream来实现序列化/反序列化传输,也可以通过FileInputStream/FileOutputStream来实现文件传输。
反射
反射时在类运行时获取其信息的一种技术,具体方式是获取字节码文件(也就是.class文件)对象,然后从对象中获取该类的构造器、变量、方法等成员,且能无视修饰符强行访问。
获取字节码的三种方式:
- 对象.getClass()
- 类名.class
- Class.forName(String classname) classname是全类名
因为第三种才能获取较为完整的对象,一般使用第三种。
反射获得构造方法的方式有:
1 | |
获得构造方法后可以创建对象。
Object o = constructor.newInstance("张三", 20, true);
如果构造器非public修饰,需要暴力破解。
constructor.setAccessible(true);
反射获得成员变量的方式有:
1 | |
获得成员变量后可以给成员变量赋值。
1 | |
也可以获得某个成员变量。
Object o1 = nameFiled.get(o);
如果成员变量非public修饰,需要暴力破解,方式同上。
反射获得成员方法的方式有:
1 | |
可以利用Method调用对象方法。
Object invoke(Object obj, Object... args);
第一个是要调用的对象,后面是多个可变参数类型。
Class类API:
1 | |
Field类API:
1 | |
Method类API:
1 | |
配置文件
配置文件(properties)里有各种配置信息,以键值对方式存储。
配置文件中注释使用#号。
Properties类表示了一个持久的属性集,可以获得配置文件中的数据。
一种构造方法:
Properties() 创建一个无默认值的空属性列表
两种成员方法:
1 | |