Ykuri98
文章46
标签14
分类1
学习总结(2022.04.12-2022.04.20)

学习总结(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中的优先级是静态的,只能给操作系统一个建议,实际上操作系统有自己的一套线程优先级

多线程的实现方式

  1. 定义一个类继承Thread类,重写run()方法,创建该类对象,通过start()创建线程(如果使用run()只是普通的调用子类方法,并不能达到创建多线程的效果)特点:单继承
  2. 定义一个类实现Runnable接口,重写run()方法,创建Runnable子类对象,再创建Thread对象,并将该子类对象作为参数传递。特点:实现接口
  3. 定义一个类实现Callable接口,重写call()方法,创建FutureTask对象并将该类的对象作为参数传递,再创建Thread对象将FutureTask对象作为参数传递。特点:有返回值

线程API

1
2
3
4
5
6
7
8
9
String getName();// 获得该线程的名称
void setName(String name);// 设置该线程的名称
Static Thread currentThread();// 返回当前正在执行的线程对象的引用
static void sleep(long millis);// 线程休眠,进入阻塞,但是不释放资源
void join();// 主线程等待执行该方法的子线程终止。join在哪个线程的代码块中被调用,该线程就是主线程;哪个线程调用了join,该线程就是子线程
void setDaemon(boolean on);// 设置一个线程为守护线程。当正在运行的线程都为守护线程时,jvm停止运行;该方法必须在线程启动前调用;GC线程是守护线程。
void wait();// 线程进入阻塞并释放资源。
void notify();// 线程唤醒随机一个阻塞的进程。
void notifyAll();// 线程唤醒全部进入阻塞的进程。

安全中断线程

线程执行完毕就会中断。但是很多时候我们需要自己控制线程终止,然而API中的stop()方法存在线程不安全问题,所以一般定义一个成员变量boolean flag来控制。

synchronized关键字

基本语法

1
2
3
synchronized(锁对象){
// 对共享数据的访问操作
}

锁对象可以是:

  1. 任意一个Object及其子类对象,java中的任意对象都可以当锁,都存在wait()、notify()和notifyAll()方法。
  2. this关键字,即是令包含该代码块的类对象当锁。
  3. 字节码文件对象(类名.class)

Lock类

成员方法:

1
2
lock();// 获取锁
unlock();// 释放锁

lock是一把真正的锁类,可以让我们看到加锁解锁的过程。

死锁的原因及解决方法

原因:一般出现在同步代码块嵌套,因为2个或以上的线程抢夺资源而造成互相等待。

1
2
3
4
5
6
7
8
9
10
synchronized(ObjA){
synchronized(objB){

}
}
synchronized(objB){
synchronized(ObjA){

}
}

解决方式:

  1. 更改加锁顺序
1
2
3
4
5
6
7
8
9
10
synchronized(ObjA){
synchronized(objB){

}
}
synchronized(ObjA){
synchronized(objB){

}
}
  1. 再加一把锁,将非原子操作改为原子操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
synchronized(ObjC){
synchronized(objB){
synchronized(objA){

}
}
}
synchronized(objC){
synchronized(ObjA){
synchronized(objB){

}
}
}

线程池

线程池有三种子类:

  1. ExecutorService newCachedThreadPool():根据需要创建新线程,也可以自动删除,60s处于空闲状态的线程。线程数量可变。
  2. ExecutorService newFixedThreadPool(int nThreads):线程数量固定,维护一个无界队列(暂存已提交的来不及执行的任务),按照任务的提交顺序,将任务执行完毕。
  3. ExecutorService newSingleThreadExecutor():单个线程,其他特点如上。

两种成员方法:

1
2
void submit(Runnable task);// 提交任务
void shutdown();// 启动顺序关闭

定时任务与定时器

Timer定时器是一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

两种构造方法:

1
2
Timer();// 创建一个新计时器
Timer(boolean isDaemon);// 创建一个新计时器,可以指定其相关的线程作为守护程序运行

四种成员方法:

1
2
3
4
schedule(TimerTask task, Date time);// 从time时间开始执行。
schedule(TimerTask task, long delay, long period);// 在延迟delay毫秒后,每period毫秒执行一次。
schedule(TimerTask task, Date firstTime, long period);// 从firstTime时间开始执行,之后每period毫秒执行一次。
scheduleAtFixedRate(TimerTask task, long delay, long period);// 设置一个继承了TimerTask类的定时任务类,在延迟delay毫秒后,每period毫秒执行一次该任务。(TimerTask定时任务,使用时继承该类并重写run()方法)

UDP/TCP

UDP发送端步骤:

  1. DatagramSocket datagramSocket = new DatagramSocket(int port); 创建发送端的Socket对象
  2. DatagramPacket sendPacket= newDatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port); 创建用于发送的数据报包
  3. datagramSocket.send(sendPacket); 把包发送出去
  4. datagramSocket.close(); 关闭Socket

UDP接收端步骤:

  1. DatagramSocket datagramSocket = new DatagramSocket(int port); 创建接收端的Socket对象
  2. DatagramPacket receivePacket = new DatagramPacket(byte[] buf, int offset, int length); 创建用于接收的数据报包
  3. datagramSocket.receive(receivePacket); receive方法进行接收
  4. byte[] data = receivePacket.getData(); 解析数据
  5. datagramSocket.close(); 关闭Socket

UDP可以通过多线程优化,达到两端互相通信的效果

TCP客户端步骤:

  1. Socket socket = new Socket(String host, int port); 创建客户端的socket对象
  2. OutputStream out = socket.getOutputStream(); 从socket中获取输入/输出流
  3. out.write(byte[] b); 利用输入/输出流进行读写操作
  4. socket.close(); 关闭Socket

TCP服务端步骤:

  1. ServerSocket serverSocket = new ServerSocket(int port); 创建服务端的ServerSocket 对象
  2. Socket socket = serverSocket.accept(); 利用accept方法建立连接,得到socket对象
  3. InputStream in = socket.getInputStream(); 从socket中获取输入/输出流
  4. in.read(byte[] b); 利用输入/输出流进行读写操作
  5. socket.close(); 关闭Socket

TCP可以通过ObjectInputStream/ObjectOutputStream来实现序列化/反序列化传输,也可以通过FileInputStream/FileOutputStream来实现文件传输。

反射

反射时在类运行时获取其信息的一种技术,具体方式是获取字节码文件(也就是.class文件)对象,然后从对象中获取该类的构造器、变量、方法等成员,且能无视修饰符强行访问。

获取字节码的三种方式:

  1. 对象.getClass()
  2. 类名.class
  3. Class.forName(String classname) classname是全类名

因为第三种才能获取较为完整的对象,一般使用第三种。

反射获得构造方法的方式有:

1
2
3
4
Constructor[] getConstructors();// 获取所有public方法
Constructor[] getDeclaredConstructors();// 获取所有构造方法,包括private
Constructor<T> getConstructor(Class<?>... parameterTypes);// 获取单个public方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);// 获取单个构造方法,包括private

获得构造方法后可以创建对象。

Object o = constructor.newInstance("张三", 20, true);

如果构造器非public修饰,需要暴力破解。

constructor.setAccessible(true);

反射获得成员变量的方式有:

1
2
3
4
Field[] getFields();// 获取所有public变量
Field[] getDeclaredFields();// 获取所有成员变量
Field getField(String name);// 获取指定public成员变量
Field getDeclaredField(String name);// 获取指定成员变量

获得成员变量后可以给成员变量赋值。

1
2
Object o = declaredConstructor.newInstance();// 实例化对象
nameFiled.set(o, "张三");// 对该对象的名字成员变量赋值

也可以获得某个成员变量。

Object o1 = nameFiled.get(o);

如果成员变量非public修饰,需要暴力破解,方式同上。

反射获得成员方法的方式有:

1
2
3
4
Method[] getMethods();// 获取所有public方法
Method[] getDeclaredMethods();// 获取所有方法
Method getMethod(String name, Class<?>... parameterTypes);// 获取指定public方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes);// 获取指定方法

可以利用Method调用对象方法。

Object invoke(Object obj, Object... args);

第一个是要调用的对象,后面是多个可变参数类型。

Class类API:

1
2
3
4
String getName();// 获取全类名
String getSimpleName();// 获取简单名
Class<?> getInterfaces();// 获取接口
Class<?> getSuperclass();// 获取父类

Field类API:

1
2
Class<?> getType();// 获取变量类型
int getModifiers();// 获取变量类型,以int值表示,可用Modifier.toString()转为String

Method类API:

1
2
String getReturnType();// 获取返回值类型
Class<?>[] getParameterTypes();// 获取方法参数

配置文件

配置文件(properties)里有各种配置信息,以键值对方式存储。

配置文件中注释使用#号。

Properties类表示了一个持久的属性集,可以获得配置文件中的数据。

一种构造方法:

Properties() 创建一个无默认值的空属性列表

两种成员方法:

1
2
void load(InputStream inStream);// 从输入流中读取属性列表(键和元素对)
String getProperty(String key);// 用指定的键在此属性列表中搜索属性
本文作者:Ykuri98
本文链接:https://ykuri98.github.io/2022/04/18/%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%EF%BC%882022-04-12-2022-04-18%EF%BC%89/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可
×