10.Java基础之IO+注解+反射+socket

0.回顾

  1. File的常用方法
  2. 其他略

1.IO流

1.1 IO流的简介

对于文件的读写操作需要使用IO流,Input和Output。

分类:

  1. 单位划分:

    ①字节流:InputStream和OutputStream,可以处理任何类型的文件

    ②字符流:Reader和Writer,文本文件 txt

  2. 流向划分

    ①输入流:InputStream和Reader

    ②输出流:OutputStream和Writer

    输入和输出是相对而言的,存储设备—》内存 ,读的过程,输入流;内存—》存储设备,写的过程,输出流

  3. 角色划分

    ①节点流

    ②处理流

一个宗旨:IO流中只有四个抽象基类:InputStream、OutputStream、Reader、Writer

1.2 OutPutStream

通常使用FileOutputStream

常用的构造方法:

FileOutputStream(String name):创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean append):创建文件输出流以指定的名称写入文件。

若使用第一个构造方法,每次写的内容都会覆盖(从头开始写)

若使用第二个构造方法,而且true,则向文件末尾追加内容

写的方法:

void write(byte[] b) :将b的数组写入到文件中,一般通过"zzzz".getBytes()
void write(byte[] b, int off, int len) :将b的数组,可以指定开始下标和长度
void write(int b) :一个一个字节的写 ----》基本不用。

注意:

①IO中流是一种资源,所以使用完之后,要将流关闭。

②若文件不存在,会自动创建

1.3 InputStream

通常使用:FileInputStream

常用的构造方法:

FileInputStream(String name)

常用的读取的方法:

int read() :从该输入流读取一个字节的数据。 效率较低,基本不用。
int read(byte[] b) :直接读取一个数组的数据量,类似shopping时的购物车。

注意:若文件内容正好是byte数组的整数倍,没有问题;若不是整数倍,内容会有错误

原因:byte[]数组,采用是覆盖策略,而不是清空策略;每次取的都是数组长度

解决:我们应该取读取的有效长度,而是不数组长度

//一个一个读取
int read;
while ((read=fis.read()) != -1) {
    System.out.println((char)read);
}

byte[] buf = new byte[6];
//        is.read(buf);
//        System.out.println(new String(buf));
//        is.read(buf);
//        System.out.println(new String(buf));
int len;    //保存真实读取的缓冲数组的长度
while ((len=is.read(buf)) != -1) {
    System.out.println(new String(buf,0,len));
}

至于缓冲数组取多大,一般取决于文件大小,通常对于大文件,一般设置为1024的整数倍

int read(byte[] b, int off, int len) :读取数组的指定长度的数据量。

2.Reader和Writer

这两个抽象类,主要处理文本文件。适用范围没有字节流广泛。

2.1 Writer

常用的实现类:FileWriter

FileWriter的常用构造方法:

FileWriter(File file,[boolean append])

FileWriter(String filePath,[boolean append])

常用的方法:

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)

void close():为了避免空指针异常,应该判断是否为空,再去关闭

2.2 Reader

常用的实现类:FileReader

FileReader的常用构造方法:

FileWriter(File file)

FileWriter(String filePath)

常用的方法:

int read() :读一个字符,几乎不用
int read(char[] cbuf) :将字符读入数组。
abstract int read(char[] cbuf, int off, int len) :将字符读入数组的一部分

补充练习:实现文件内容的替换

大致需求如下:

源文件a.txt内容:我叫{name},来自{address}
目标文件b.txt内容:我叫{张三},来自{北京}

2.3 补充

System.out 返回值是PrintStream,本质还是一个OutPutStream,调用的PrintStream的print/println方法进行打印

ctrl+alt+b:查看某个类的实现类或子类

3.BufferedXxx和转换流

明确:在原有的基础流之上,又包裹了一层,所以,构造BufferedXxx系列的时候,需要传入对应的字节流和字符流对象

通过源码发现,里面的很多方法,都是使用synchronized修饰,线程安全的,

3.1 BufferedInputStream和BufferedOutputStream

//使用BufferedXxx缓冲流实现文件的复制
@Test
public void testBuffed() throws Exception{
    InputStream is = new FileInputStream("f:\\software\\VMware-workstation-full-14.1.5-10950780.exe");
    //1.需要创建缓冲流对象
    BufferedInputStream bis = new BufferedInputStream(is);
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("3.exe"));

    long startTime = System.currentTimeMillis();
    //2.实现文件的copy
    //读写过程
    byte[] buf = new byte[1024*10];
    int len;    //用于记录读取到的数组元素的真实长度
    while ((len = bis.read(buf)) != -1) {
        bos.write(buf,0,len);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("耗时:"+(endTime-startTime));
}

3.2 BufferedReader和BufferedWriter

练习:

1,张三,18
2,李四,19

需求:在a.txt存储的是学生信息(学号、姓名、年龄)
    将学生信息从a.txt中读取出来,封装成Student
    然后存储到ArrayList或hashmap<Integer,Stu>中,
    ①遍历出来【使用Stream流处理】
    ②计算sum、avg、max、min
    ③找出姓名中有三的人有几个
    ④过滤出年龄大于10的人有哪些
使用BufferReader处理
package exer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
 * @author azzhu
 * @create 2019-12-28 10:40:55
 */
public class TestBufferRW {
    public static void main(String[] args) {
        List<Stu> stus = new ArrayList<>();
        Map<Integer,Stu> map = new HashMap<>();
        try(BufferedReader br = new BufferedReader(new FileReader("d:\\a.txt"));
            BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\b.txt"))
        ) {
           //读写过程,一行一行读取
           String line = null;
           while ((line = br.readLine()) != null) {
               //需求处理line,将其切割出来,设置到对象的属性值
               String[] split = line.split(",");
               Stu stu = new Stu();
               stu.set(Integer.parseInt(split[0]),split[1],Integer.parseInt(split[2]));
                //将stu放入到集合中 ArrayList/Map
               stus.add(stu);
               map.put(stu.getId(),stu);

               //将line原样写出去
               bw.write(line);
               bw.flush();
           }
            //遍历list【过滤age>18,最大age】和map
            stus.forEach(System.out::println);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Stu{
    private Integer id;
    private String name;
    private int age;

    public void set(Integer id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

3.3 备注

  1. 在使用输出流的时候,使用flush进行刷新,保证内容全部过去
  2. 流的关闭顺序:先用后关,后用的先关
  3. 关闭流的时候,应该在finally块中关闭,而且要判断非空

3.4 转换流

将字节流转换为字符串流:InputStreamReaderOutputStreamWriter

4.序列化和反序列化[了解]

目的:

①内存中的对象能否持久化保存?

②能否将对象在网络中进行传递?能否将对象保存到文件中

注意:序列化多个对象到文件中,读取的时候要避免如下一些异常:EOFExceptionOptionalDataException

序列化:将内存中的对象写到文件中

反序列化:将文件中的对象读取出来

前提:若是自定义的对象,必须实现Serializable,若没有实现该接口,抛出如下异常java.io.NotSerializableException: obj.Stu

我们使用ObjectInputStream和ObjectOutputStream

  1. 构造方法

    ObjectInputStream(InputStream in)

    ObjectOutputStream(OutputStream out)

  2. 常用的方法:readXxx和writeXxx

注意:

  1. 写和读什么类型的数据,使用具体的writeXxx和readXxx方法
  2. 写入的先后顺序应该跟读取的一致,即先写的先读出来(先进先出–队列)
/**
 * 实现序列化和反序列化
 * @author azzhu
 * @create 2019-12-28 10:57:36
 */
public class TestObjectXxxStream {

    @Test
    public void testRead() {
        try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\c"))) {
            Object obj = ois.readObject();
            Stu stu = (Stu) obj;
            System.out.println(stu);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testRead2() {
        try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\c"))) {
            Object obj = ois.readObject();
            List<Stu> stus = (List<Stu>) obj;
            System.out.println(stus);

            int value = ois.readInt();
            System.out.println(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSave2() {
        List<Stu> list = new ArrayList<>();
        try(ObjectOutput oos = new ObjectOutputStream(new FileOutputStream("d:\\c"));) {
            Stu stu = new Stu();
            Stu stu2 = new Stu();
            stu.set(1001,"zs",20);
            stu2.set(1002,"zss",28);
            list.add(stu);
            list.add(stu2);
            //写一个集合到文件中
            oos.writeObject(list);
            oos.writeInt(10);
            oos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSave() {
        try(ObjectOutput oos = new ObjectOutputStream(new FileOutputStream("d:\\c"));) {
            Stu stu = new Stu();
            stu.set(1001,"zs",20);
            oos.writeObject(stu);
            oos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}@Test
public void testRead() throws Exception {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));
    int num = ois.readInt();
    Object obj = ois.readObject();

    System.out.println(obj);
    System.out.println(num);
    ois.close();
}
@Test
public void testWrite() throws Exception{
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));

    Stu stu = new Stu(1001, "张三", 18);
    oos.writeObject(stu);
    oos.writeInt(10);
    oos.flush();
    oos.close();
}

5.注解[扩展了解]

  1. 明确:不写通用或者封装框架等底层代码,你用不到
  2. 能看懂注解(注解可以在哪使用)

注解:具有指定意义的标识符,有自己的语法格式

  1. 掌握自定义直接的格式
@interface
  1. jdk常见的几种注解,比如@override/@Deprecated/@SuppressWarnings

  2. 注解可以在哪些地方使用

    观察:@Target({ElementType.METHOD})

  3. 在哪个时刻使用,运行/编译等等

    观察:@Retention(RetentionPolicy.RUNTIME),一般使用Runtime

  4. 元注解/元信息/元数据

    元注解:对我们的注解进行的说明/详细说明/细分的注解

    元数据:xx.doc,存储位置、大小、后缀名、作者…

6.反射[掌握]

通过反射:可以洞悉类的一切内容,包括私有,Class

需要掌握的:

  1. 获取Class的实例 —获取类的内容,都通过这个实例

    Class.forName(全类名)

  2. 对于方法的调用

    Method类型 m m.invoke(对象,参数列表)

  3. 对于构造器

    使用构造器创建对象

    //注意,应该根据构造方法的参数个数去调用对应的构造方法
    if(constructor.getParameterCount() == 3) {
        Object obj = constructor.newInstance(1001, "理论", 20);
        System.out.println(obj);
    }
    
  4. 获取其他信息,比如实现了哪些接口、继承了哪些类、使用了哪些注解、获取类的泛型、为私有属性赋值…

package test08.TestReflect;

import org.junit.Test;

import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.ArrayList;

/**
 * @author azzhu
 * @create 2019-12-28 13:58:03
 */
public class TestAnnotation {

    //获取某个类实现的接口
    @Test
    public void test08() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        Class[] interfaces = clazz.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println(anInterface.getName()+":"+anInterface.getSimpleName());
        }
    }

    //获取方法上的注解
    @Test
    public void test07() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        Method m1 = clazz.getMethod("m1", null);
        Annotation[] annotations = m1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.annotationType());
        }
    }

    //获取到注解
    @Test
    public void test06() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
//            if(annotation.annotationType().getClass()==MyAnnotation.class.getClass()) {
//                //做一些处理
//            }
            System.out.println(annotation.annotationType());
        }
    }

    //获取父类,以带泛型的父类 *****
    @Test
    public void test5() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        //获取父类
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass.getName());   //java.util.ArrayList
        //获取带泛型的父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        //java.util.ArrayList<test08.TestReflect.Stu>
        System.out.println(genericSuperclass.getTypeName());

        //获取父类中的泛型
        ParameterizedType parameterizedType = (ParameterizedType)genericSuperclass;

        //获取参数化类型的真实参数:test08.TestReflect.Stu
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.printf(actualTypeArgument.getTypeName());
            //可以构建一个泛型对象 stu
        }
    }

    //操作属性
    @Test
    public void test4() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        Object obj = clazz.newInstance();
        Field field = clazz.getDeclaredField("age");
        field.setAccessible(true);
        field.setInt(obj,10);

        System.out.println(((Stu)(obj)).getAge());
    }

    //获取构造方法,以及构建对象
    @Test
    public void test3() throws Exception{
        Class clazz = Class.forName("test08.TestReflect.Stu");
        Constructor[] constructors = clazz.getConstructors();
       // System.out.println(constructors.length);
        Object obj = null;
        for (Constructor constructor : constructors) {
            //调用有参构造创建对象
            if(constructor.getParameterCount() > 0) {
                obj = constructor.newInstance(10);
            }
        }
        System.out.println(((Stu)obj).getAge());
    }

    //获取Class实例的方式
    @Test
    public void test2() throws Exception {
        //1.获取Class实例的方式1:常用的
        //Class clazz = Class.forName("test08.TestReflect.Stu");

        //2.通过类名去获取
        Class<Stu> stuClass = Stu.class;

        //3.通过对象获取:有些方法或属性无法操作
        Stu<Stu> stu = new Stu<>();
        Class<? extends Stu> clazz = stu.getClass();
    }

    //需要获取Class实例:体验版
    @Test
    public void test1() throws Exception{
        //test08.TestReflect.Stu
        //1.获取Class实例的方式1:常用的
        Class clazz = Class.forName("test08.TestReflect.Stu");

        //2.通过clazz创建对象
        Object obj = clazz.newInstance();
        //======方法操作
        //1.获取所有方法
        //Method[] methods = clazz.getMethods();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //System.out.println(method.getName());
            //System.out.println(method.getReturnType());
            if("m2".equals(method.getName())) {
                method.setAccessible(true);
                method.invoke(obj,10);
            }
        }
    }
}

@MyAnnotation({"aa","bb"})
class Stu<Integer> extends ArrayList<Stu> implements Serializable {
    private int age;

    public Stu(){}
    private void m2(int a) {
        System.out.println("private method"+a);
    }

    public int getAge() {
        return age;
    }

    public Stu(int age) {
        this.age = age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @MyAnnotation("m1")
    public void m1() {

    }
}

@Target({ElementType.METHOD,ElementType.TYPE})  //我们的注解可以使用的地方
@Retention(RetentionPolicy.RUNTIME)
//@Deprecated
@interface MyAnnotation {
    String[] value();
}

7.socket编程[了解]

需求:模拟客户端可以一直给服务端发送消息,服务端显示客户端发送过来的消息。

TCP、IP四层协议 vs OSI七层模型对比

在这里插入图片描述

【面试】TCP和UDP的区别

①TCP:面向连接,安全可靠,数据不会丢失,文件下载,TCP的3次握手过程

②UDP:面向无连接,不保证数据是否丢失

网络通信三要素:协议 + IP地址 + 端口号

端口号:用两个字节表示的整数,它的取值范围是0~65535

面试题:http和https的区别

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点 ,理解为端口号

ServerSocket:创建这个对象的时候,只需要传入端口号

Socket:需要ip地址/主机名+端口号

8.xml[了解]

需要掌握的内容:

  1. xml文件的作用:配置文件
  2. 知道xml文件里面的标签,通常是有约束的
  3. dom4j,知道是干啥用的即可,无需走一遍CRUD
  4. dom解析 vs sax解析的对比 ----了解

Extended Markup Language:可扩展标记/标签语言。文件的后缀名:.xml

标签:可以在里面封装内容

标签有2种

①需要在标签中封装(写)内容:一种有开始,有结束<age>19</age>

②不需要在标签中写内容:<aa/>

对于一些特殊字符的处理,若内容很多,可以将其放在 CDATA块中

<![CDATA[" 开始,由 "]]>

xml文件,里面的标签,通常都不是乱写的,通常是有限制的,我们称之为schema约束,或者是通过dtd文件进行约束

用处:

  1. 可以当成小型数据库使用,即存储数据
  2. 数据传输的格式【使用很少】,我们后面会使用json格式进行传输
  3. 配置文件

如何自定义一个xml文件[了解]

<?xml version="1.0" encoding="UTF-8" ?>  <!--第一行是声明,告诉我们这是一个xml文件-->
<stus>
    <!--通过自定义标签,存储数据,
    1.如何将数据取出来封装成对象,保存到list中
    2.如何将List/stu写到文件中
	3.一切解析:先将xml文件变为Document对象,一切解析从根stus开始
    1和2就是解析文件的问题:
    有如下几种方式:
    ①jdk提供的原生方式,比较麻烦
    ②使用第三方框架 dom4j https://dom4j.github.io/,后面会学框架,实际一些框架对于xml文件的解析使用的就是dom4j

    【小概率面试】xml两种解析方式的区别
    dom解析 sax解析【Simple Api xml】
    dom解析:将整个文档解析成一颗dom树,加载进内存,
    若文件很大,会非常耗费内存
    优点:对于查找某个节点非常方便,可以重复获取节点信息
    sax解析:边读边解析,对内存消耗不大
    缺点:对于多次获取某个标签的需求,需要重新读取

    dom4j结合上述两种解析的优点
    3.目前我们自定义的这个文件的标签很随意,没有任何的限制
        实际上应该有一个约束,schema约束
        即在我们后面写配置文件的过程中,不是任意标签都行
    -->
    <stu id="1001">
        <name>张三</name>
        <age>19</age>
    </stu>
    <stu id="1001">
        <name>张三</name>
        <age>19</age>
    </stu>
</stus>

在这里插入图片描述

9.异常处理补充

  1. 之前我们关闭资源都是在finally块中,可以有一个简化的替代品:try-with-resource
  2. 还可以使用JDK7优化后的 try-with-resource 语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource)是指在程序完成后,必须关闭的对象。
@Test
public void testFileCopy2() {
    try(FileInputStream fis = new FileInputStream("F:\\桌面\\VV1.rar");
        FileOutputStream fos = new FileOutputStream("d:\\a.rar")
       ){
        //1.创建输入流
        //2.读写过程
        long startTime = System.currentTimeMillis();
        byte[] buf = new byte[1024*2];
        int len = 0;    //缓冲数组中真实的内容长度
        while ((len=fis.read(buf)) != -1) {
            //写的过程
            fos.write(buf,0,len);
            fos.flush();
        }
        long endTime = System.currentTimeMillis();
        System.out.printf("耗时:"+(endTime-startTime));
    }catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void testFileCopy() {
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try{
        //1.创建输入流
        fis = new FileInputStream("F:\\桌面\\VV1.rar");
        fos = new FileOutputStream("d:\\a.rar");
        //2.读写过程
        long startTime = System.currentTimeMillis();
        byte[] buf = new byte[1024*2];
        int len = 0;    //缓冲数组中真实的内容长度
        while ((len=fis.read(buf)) != -1) {
            //写的过程
            fos.write(buf,0,len);
            fos.flush();
        }
        long endTime = System.currentTimeMillis();
        System.out.printf("耗时:"+(endTime-startTime));
    }catch (Exception e) {
        e.printStackTrace();
    } finally {
        //3.关闭流
        try {
            if(fos != null) {
                fos.close();
            }
            if(fis != null) {
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

JDK9中 try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,我们来了解一下格式。

在这里插入图片描述

10.Properties属性集

注意:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。

需求:写一个配置文件jdbc.properties

username=zs
pwd=111

通过Properties这个类,获取username的value

public class TestProperties {
    public static void main(String[] args) {
        try(FileInputStream fis = new FileInputStream("D:\\IDEA\\226\\day11_java\\src\\test05\\jdbc.properties");) {
            Properties prop = new Properties(); 
            //加载流
            prop.load(fis);
            //get(key):获取指定key的value值 *****
            System.out.println(prop.get("username"));

            System.out.println("==================");
            //取到key的集合
            Set<String> set = prop.stringPropertyNames();
            for (String key : set) {
                Object value = prop.get(key);
                System.out.println(key +":"+value);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

11. 练习

  1. 流的使用

    假如d:/a.txt文件中有以下内容:
    编号 名称 价格  数量  Product
    01,咖啡,20.5,10
    02,伴侣,30.8,2
    03,咖啡杯,28,5
    04,勺子,5.5,5
    
    需求:
    1、写程序读取文件中的数据
    2、然后将商品信息按照价格由高到低排序
    3、将排序结果输出到一个文件中:d:/price_order.txt
    4、然后按成交总金额排序,并将排好的数据输出到文件:d:/amount_order.txt
    5、最后输出整个文件中的商品信息的汇总成交金额,打印在控制台
    01,咖啡,20.5,10 205
    02,伴侣,30.8,2  xx
    03,咖啡杯,28,5
    04,勺子,5.5,5
    
    bufferedreader
    bufferedwriter
    arraylist
    product
    collections.sort()
    
  2. 完成图片/音频/视频文件的复制,分别使用FileInputStream/FileOutputStream和缓冲流,结合缓冲数组大小的不同,测试耗费的时间

  3. 使用FileWriter和FileReader,实现文件的复制,同时做一些修改,具体如下

    源文件a.txt内容:我叫{name},来自{address} 
    目标文件b.txt内容:我叫{张三},来自{北京}
    tips:a.txt的内容,放到StringBuilder/String中,replace() ---> 新的结果,写出去
    
  4. 需求:使用对象流,从某个文件中读取出stu的信息,然后封装成ArrayList,将ArrayList写出大到文件中;从序列化文件中,读取出stus信息,在将每个学生的信息写到某个文本文件中,格式跟原始数据一样

    属性说明:学号 姓名 年龄

    1001,张三,20
    1001,张三,20
    1001,张三,20
    
  5. 反射API的使用

    ①属性相关

    ②方法相关

    ③构造器相关

    ④带泛型的父类相关

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值