20220812 第六组 张嘉源 学习笔记二
IO流
输入输出流
读为输入,写为输出(相对于应用程序而言)
特点
先进先出:最先写入输出流的数据最先被输入流读取到
顺序存取:一个接一个写入或读取,不能随机访问
只读或只写:在一个数据传输通道中,如果既要写入数据,又要读取数据,则分别提供两个流
分类
1.按数据流的方向:
输入流:从硬盘上读取数据到内存
输出流:从内存写数据到硬盘
2.按操作单元分:
字节流:二进制操作,一个字节一个字节的操作,操作任意类型的文件,如图像,音频,视频,PPT,word等
字符流:一个字符一个字符的操作,一个字符两个字节,主要用来处理文本文件,如: .txt,.java,.py,.xml,.html,.css,.js,.properties
3.按功能:
节点流:直接操作一个特定的IO设备
处理流:在节点流的基础上做进一步的处理
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BUfferedWriter |
操作对象 | ObjectInputStream | ObjectInputStream |
字节输入流
步骤
1.创建一个FileInputStream对象
2.定义一个标记,用来控制输入流的读取
3.循环读取,如果读取到了-1,说明读取到了文件的末尾,循环结束
4.关闭资源
注意:当一个流读取完之后会默认调用mark和reset方法来进行记录和重置,这个流就已经重置到了上次读完的位置,所以就无法再次读取内容,并不是读完一次之后就关闭了流。
字节输出流
FileOutputStream构造器:
boolean append参数:如果传入true,则代表在原有基础上追加,不覆盖,如果传入false或者不传,覆盖原有内容
写的操作,目标文件如果不存在,会自动新建
文件的复制
两个流
FileInputStream:把对应文件的内容读取出来
FileOutputStream:把读到的内容写出去
read(byte[])
write(byte[])
多个流同时使用时:先用的流后关,后用的先关!!!
序列化和反序列化
序列化:把Java对象转换为字节序列的过程。将对象写到IO流中,将内存模型的对象变成字节数字,可以进行存储和传输。
反序列化:把字节序列恢复为Java对象的过程。从IO流中恢复对象,将存储在硬盘上或者从网络中接受的数据恢复成对象模型
作用
序列化最重要的作用:在传递和保存对象时,保证对象的完整性和可传递性,对象转换为有序字节流,以便在网络上传输或者保存在本地文件中
反序列化最重要的作用:
根据字节流中保存的对象状态及描述信息,通过反序列化重建对象
使用场景:
所有可在网络上传输的对象都必须时可序列化的,否则会报错
所有保存在硬盘上的对象也必须要可序列化
序列化版本号
反序列化必须拥有class文件,但随着项目的升级,class文件也会升级
序列化保证升级前后的兼容性。
java序列化提供了一个版本号,版本号是可以自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,所以无法匹配,则报错!!!
不指定版本号,还有一个隐患,不利于JVM的移植,可能class文件没有改,但是不同的jvm计算规则不同,导致无法反序列化
如果只修改了方法,反序列化不受影响,无需修改版本号
修改了静态变量static,瞬态变量transient,反序列化也不受影响,无需修改版本号
所有需要网络传输的对象都需要实现序列化接口
对象的类名、实例变量都会被序列化;方法、类变量、transient变量不会被序列化
如果不想让某个变量不被序列化,可以用transient修饰
序列化对象的引用类型成员变量,也必须是可序列化的,否则会报错
反序列化时必须有序列化对象的class文件
同一个对象被序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化的版本号
注解
Annotation,又叫元数据,一种代码级别的说明
注解分类
- 元注解:专门给注解加的注解
- JDK自带注解:10个,4个注解在java.lang.annotation中,6个在java.lang中
- 自定义注解
元注解
@Retention注解
标识这个注解的作用域:源文件中、字节码文件中、运行中
@Documented注解
标记这个注解是否包含在用户文档中
@Target注解
这个注解可以修饰哪些信息:类上、方法上、属性上
@Inherited注解
允许子类继承这个注解
@Repeatable注解
JDK8引入,表示标记的注解可以多次应用于相同的声明或类型
自定义注解—方法注解
定义方法的格式:String name();
可以有默认值,也可以没有,如果没有默认值在使用的时候必须使用default指定默认值
注解的3个重要主干类
Annotation
接口,定义一些常用的方法
ElementType
枚举,它用来指定注解的类型,也就是描述注解存在的位置
RetentionPolicy
枚举,用来指定注解的策略,即该注解的有效期,不同类型的策略指定的注解作用域不同
Annotation和RetentionPolicy是一对一的关系,即每个注解只能有一种保留策略
SOURCE
在源文件中有效,仅存在于编译期处理期间(即源文件保留)
CLASS
在class文件中有效(即class保留)
RUNTIME
在运行期有效(即运行时保留)
注解的作用分类
- 编写文档:通过代码里的标识的元数据生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查:通过代码里表示的元数据让编译器能够实现基本的编译检查【Override等】
作用在代码上的注解
@Override注解
标记在成员方法上,用于标识当前方法是重写父类(父接口)方法,编译器在对该方法进行编译时会检查是否符合重写规则,如果不符合,编译报错
@Deprecated注解
用于标记当前类、成员变量、成员方法或者构造方法过时或者如果开发者调用了别标记为过时的方法,编译器在编译器进行警告
@SupperssWarnings注解
压制警告注解,可放置在类或者方法上,该注解的作用是阻止编译器发出某些警告信息
该注解为单值注解,只有一个value参数,该参数为字符串数组类型,常用参数值有:
- unchecked:未检查的转化,如集合没有指定类型还添加元素
- unused:未使用的变量
- resource:有泛型未指定类型
- path:在类路径,原文件路径中又有不存在的路径
- 使用了某些不赞成使用的类和方法
- fallthrough:switch语句执行到底没有break关键字
- rawtypes:没有写泛型,比如List list = new ArrayList();
- all:全部类型的警告,如@SupperssWarnings("all")
@SafeVarargs注解
JDK7开始支持,忽略任何使用参数为泛型变量的方法或构造器产生的警告
@FunctionalInterface注解
JDK8开始支持,表示一个接口为函数式接口
@Repeatable注解
JDK8开始支持,表示标记的注解可以多次应用于相同的声明或类型
常用参数:
all:忽略所有警告
boxing:忽略装箱、拆箱警告
rawtypes:使用生成时没有指定数据类型
unchecked:忽略没有进行类型检查操作的警告
unused:忽略没有使用的警告
反射
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的功能称为Java的反射机制
获取class对象的三种方式
Dog类
@Bean
public class Dog {
public String type;
private String name;
private String color;
public Dog() {
}
private Dog(String name){
this.name = name;
}
public Dog(String name, String color) {
this.name = name;
this.color = color;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@MyAnnotation(value = 20,age=10)
public void show(String str) {
System.out.println("show方法..." + str);
}
public String info() {
return "info方法";
}
private void fun() {
System.out.println("私有的fun方法...");
}
}
1.Object->getclass();
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
public class Ch02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 获取类对象
// 1.直接使用类名.class
Class<Dog> clazz = Dog.class;
// 对类对象操作
// 获取类的名字
System.out.println(clazz.getName());
// 获取类的加载器
ClassLoader classLoader = clazz.getClassLoader();
System.out.println(classLoader);
// 获取资源
URL resource = clazz.getResource("");
// 获取父类
System.out.println(clazz.getSuperclass());
// 判断一个类是不是接口,数组
System.out.println(clazz.isArray());
System.out.println(clazz.isInterface());
// 重点,使用class类对象实例化一个对象
@SuppressWarnings("all")
Dog dog = clazz.newInstance();
// clazz.getDeclaredConstructor().newInstance();
// 2.使用全类名
// Class aClass = Class.forName("morning.Dog");
// 3.使用对象
// Dog dog = new Dog();
// Class aClass1 = dog.getClass();
}
}
2.任何数据类型(包括基本数据类型)都有一个"静态"的class属性
3.通过class类的静态方法:forName(String className)
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
对成员变量的操作
import java.lang.reflect.Field;
import java.util.Arrays;
public class Ch03 {
public static void main(String[] args) throws NoSuchFieldException {
// 对成员变量的操作
Class<Dog> clazz = Dog.class;
// 只能获取到public的属性
Field type = clazz.getField("type");
System.out.println(type);
Field[] fields = clazz.getFields();
System.out.println(Arrays.toString(fields));
// 可以获取到private属性
Field name = clazz.getDeclaredField("name");
System.out.println(name);
System.out.println(name.getType());
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
}
}
获取对象的属性
import java.lang.reflect.Field;
/**
* 获取对象的属性
*/
public class Ch04 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Dog dog = new Dog();
dog.setType("金毛");
Dog dog1 = new Dog();
dog1.setType("泰迪");
Class<Dog> clazz = Dog.class;
Field type = clazz.getDeclaredField("type");
// 想要获取哪一个对象的color
String str = (String) type.get(dog1);
System.out.println(str);
}
}
import java.lang.reflect.Field;
public class Ch05 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Dog dog = new Dog();
dog.setType("萨摩耶");
Class clazz = Dog.class;
Field type = clazz.getDeclaredField("type");
type.set(dog,"拉布拉多");
System.out.println(dog.getType());
Field color = clazz.getDeclaredField("color");
// 暴力注入
color.setAccessible(true);
color.set(dog,"black");
System.out.println(dog.getColor());
}
}
使用反射调用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Ch06 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
Class dogClass = Class.forName("com.jsoft.morning.Dog");
Dog dog = (Dog) dogClass.newInstance();
Class<Dog> clazz = Dog.class;
Method show = clazz.getMethod("show", String.class);
// System.out.println(show.getParameterCount());
// System.out.println(show.getName());
Class<?>[] parameterTypes = show.getParameterTypes();
// System.out.println(Arrays.toString(parameterTypes));
// 执行方法,使用反射调用方法
show.invoke(dog,"hello");
Method[] methods = clazz.getMethods();
Method fun = clazz.getDeclaredMethod("fun");
fun.setAccessible(true);
fun.invoke(dog);
Method[] declaredMethods = clazz.getDeclaredMethods();
Method info = clazz.getDeclaredMethod("info");
String o = (String) info.invoke(dog);
System.out.println(o);
}
}
通过反射调用构造器
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Ch07 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Dog> clazz = Dog.class;
Constructor<?>[] constructors = clazz.getConstructors();
// System.out.println(Arrays.toString(constructors));
Constructor<Dog> declaredConstructor = clazz.getDeclaredConstructor();
// 通过构造器创建对象
Dog dog = declaredConstructor.newInstance();
// 单例模式
// 1.构造器私有化
Constructor<Dog> declaredConstructor1 = clazz.getDeclaredConstructor(String.class);
declaredConstructor1.setAccessible(true);
Dog dog1 = declaredConstructor1.newInstance("小强");
System.out.println(dog1.getName());
}
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.SQLOutput;
import java.util.Arrays;
public class Ch08 {
public static void main(String[] args) throws NoSuchMethodException {
Class<Dog> clazz = Dog.class;
Bean annotation = clazz.getAnnotation(Bean.class);
Annotation[] annotations = clazz.getAnnotations();
// System.out.println(Arrays.toString(annotations));
Method show = clazz.getDeclaredMethod("show", String.class);
MyAnnotation annotation1 = show.getAnnotation(MyAnnotation.class);
System.out.println(annotation1.age());
}
}
网络编程
在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
网络编程三要素
IP地址
设备的标识。网络中设备的唯一标识
查看本机IP
1.cmd指定:输入ipconfig
2.127.0.0.1
3.locklhost:需要联网
端口
网络的通信,本质上是两个应用程序的通信。端口号可以唯一标识设备中的应用程序
协议
位于同一个网络中的计算机在进行连接和通信时需要遵守的规则,常见的有UDP和TCP协议
Socket编程
建立在TCP/IP协议上
Socket通信的服务端也是一个对象:ServerSocket类
发送数据步骤
1.创建客户端的Socket对象(Socket)
2.获取输入流,写数据
3.释放资源
接收数据步骤
1.创建服务器端的Socket对象(ServerSocket)
2.监听客户端连接,返回一个Socket对象
3.获取输入流,读数据,并把数据显示在控制台
4.释放资源
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Ch01 {
@Test
public void server() throws IOException {
// 创建了一个服务器对象,绑定在8888端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器已启动...");
// 服务器在等待客户端连接
Socket socket = serverSocket.accept();
// 读取客户端发送过来的信息
InputStream inputStream = socket.getInputStream();
byte [] buffer = new byte[1024 * 1024];
int len;
while((len = inputStream.read(buffer)) != -1){
System.out.println("服务器已接收客户端的数据:" + new String(buffer,0,len));
}
// 释放资源
inputStream.close();
socket.close();
}
@Test
public void client() throws IOException {
// 创建了一个Socket通信的客户端
Socket socket = new Socket();
// 第一个参数就是获取当前电脑的IP地址
// 第二个参数就是服务器的端口号
socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),8888));
// 客户端发送数据给服务器
OutputStream outputStream = socket.getOutputStream();
String data = "hello server!";
outputStream.write(data.getBytes());
System.out.println("客户端已发送数据:" + data);
outputStream.close();
socket.close();
}
}