【Java入门到精通】(四)Java语法进阶

发布于:2025-07-15 ⋅ 阅读:(8) ⋅ 点赞:(0)

一、I/O流的使用

1.1 File类的作用

        File类对象可封装要操作的文件,可通过File类的对象对文件进行操作,如查看文件的大小、判断文件是否隐藏、判断文件是否可读等。

        局限:File类的相关操作,并不涉及文件内容相关的操作,这是单独依靠File类对象无法实现的操作,此时需要借助I/O流完成。

1.2 I/O流的作用

        将I/O流理解为一根“管子”,那么理解就会非常顺畅。I为Input,O为Output,I/O流即输入输出流,可以理解为两个流向的“管子”。

1.3 I/O流的分类

        将I/O流理解为“管子”,那么生活中“管子”怎么分类的呢?分类方式如下所示: 

方式一:按照方向划分

        输入流、输出流

方式二:按照处理单元划分

        字节流、字符流

方式三:按照功能划分

        节点流(单个流)、处理流(多个流)

1.4 I/O流的体系结构

1.5 文件输入流、文件输出流代码演示

(1)文件输入流(文件 -》 程序)
package com.study.java14;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/*
   功能:这个类用来读取 F:\Java-project\test.txt 文件的内容
 */
public class Test {
    public static void main(String[] args) throws IOException {
        // 对文件进行操作:必须将文件封装为具体的File类的对象:
        File f = new File("F:\\Java-project\\test.txt");

        // "管子"=流=输入字符流(将这个管子怼到文件上-管子和文件简历关系)
        try (FileReader fr = new FileReader(f)) {
            // int n1 = fr.read();
            // System.out.println(n1); // 当读取到文件流最后 会返回-1

            int n = fr.read();
            while (n != -1) { // 当读取到文件流最后 会返回-1
                System.out.print(n);
                n = fr.read();
            }
             // 流关闭的操作
            fr.close();
        } catch (IOException IE) {
            System.out.println("文件读取错误");
        }

    }
}
(2)文件输出流(程序 - 》 文件)
package com.study.java14;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/*
    功能:将程序中的内容输出到文件中去
 */
public class TestOut {
    public static void main(String[] args) throws IOException {
        // 程序中的字符串
        String str = "Hello World!";
        // 文件
        File file = new File("f:\\Java-project\\testOut.txt"); // 如果没有这个目录,程序会创建一个这样的目录
        // 字符输出流:
        FileWriter fw = new FileWriter(file);
        // 动作:将程序中的字符串输出到文件中
        fw.write(str);
        // 关闭流
        fw.close();

    }
}

二、项目引入I/O概念

        在我们的【小张书城】项目中,存在一个问题:就是当我们存储书籍之后,重启项目后,我们创建的书籍信息就消失了,这样使用起来是很费劲的,所以这里我们引入I/O的概念:将书籍的信息保存到文件中

2.1 项目涉及技能点

(1)对象流

        FileInputStream、FileOutputStream       

        ObjectInputStream、ObjectOutputStream(处理流)

(2)序列化

        implements Serializable

2.2 项目代码优化

Books类

package com.study.java15;

import java.io.Serializable;

public class Books implements Serializable {// 书籍相关
    // 书籍类的 属性
    // private修饰符:只允许内部访问,外部无法访问该属性
    // 书籍编号
    private int bNo;
    // 书籍名称
    private String bName;
    // 书籍作者
    private String bAuthor;
    //快捷键:alt + insert 快速创建get和set方法
    public int getbNo() {
        return bNo;
    }

    public void setbNo(int bNo) {
        this.bNo = bNo;
    }

    public String getbAuthor() {
        return bAuthor;
    }

    public void setbAuthor(String bAuthor) {
        this.bAuthor = bAuthor;
    }

    public String getbName() {
        return bName;
    }

    public void setbName(String bName) {
        this.bName = bName;
    }

    // 构造器
    public Books(int bNo, String bName, String bAuthor) {
        this.bNo = bNo;
        this.bName = bName;
        this.bAuthor = bAuthor;
    }

    // 空构造器
    public Books() {
    }
}

Test对象

package com.study.java15;

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

/*
【小张书城】项目功能规划:
1.展示书籍
2.上新书籍
  书籍信息:书籍编号    书籍名称       书籍作者
             01   小张的Java之路    小张快跑   ----》 封装成一个具体的书籍对象 ---》类:书籍
3.下架书籍
4.退出应用
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 定义文件存储的位置
        String FilePath = "f:\\Java-project\\books.txt";
        while (true){
            // 打印菜单:
            System.out.println("-------欢迎来到【小张书城】-------");
            System.out.println("1.展示书籍");
            System.out.println("2.上新书籍");
            System.out.println("3.下架书籍");
            System.out.println("4.退出应用");
            // 借助Scanner类:
            Scanner sc = new Scanner(System.in); // 扫描键盘录入事件类
            // 给一个友好的提示
            System.out.println("请录入你想要执行的功能的序号:");
            // 利用键盘,录入序号:
            int choice = sc.nextInt(); // 接收键盘录入的事件

            // 根据choice录入的功能序号,进行后续的判断
            if(choice == 1){
                System.out.println("<展示书籍>");
                // 展示书籍(对集合进行遍历查看)
                // 从文件中读取List
                File f = new File(FilePath);

                // 对文件做一个判断(判断文件是否存在)
                if(f.exists()){ // exists()用来判断文件是否存在,存在返回true,不存在返回false
                    // 使用文件流读取文件内容
                    FileInputStream fis = new FileInputStream(f);
                    try (ObjectInputStream ois = new ObjectInputStream(fis)) {

                        // 将集合做一个读取操作
                        // 将读取到的文件存到ArrayList中
                        ArrayList<Books> list = (ArrayList<Books>) (ois.readObject());
                        System.out.println(list.size());
                        for (int i = 0; i < list.size(); i++) {
                            Books b = (Books)(list.get(i)); // Java 语言中的类型转换
                            System.out.println(b.getbNo() + "------" + b.getbName() + "------" + b.getbAuthor() + "------");
                        }
                    }
                } else {
                    System.out.println("文件不存在,请创建文件");
                }
            }
            if(choice == 2){
                System.out.println("<上新书籍>");
                // 从键盘录入书籍信息
                System.out.println("请录入书籍编号:");
                int bNo = sc.nextInt(); // 获取家键盘int类型数据
                System.out.println("请录入书籍名称:");
                String bName = sc.next(); //获取家键盘String类型数据
                System.out.println("请录入书籍作者:");
                String bAuthor = sc.next(); //获取家键盘String类型数据

                // 每上新一本书,就要创建一个书籍对象
                Books b = new Books();
                b.setbNo(bNo);
                b.setbName(bName);
                b.setbAuthor(bAuthor);


                File f = new File(FilePath);
                // 对文件做一个判断(判断文件是否存在)
                if(f.exists()){ // exists()用来判断文件是否存在,存在返回true,不存在返回false
                    // 如果存在文件,需要先读取文件,再添加元素
                    // 使用文件流读取文件内容
                    FileInputStream fis = new FileInputStream(f);
                    try (ObjectInputStream ois = new ObjectInputStream(fis)) {

                        // 将集合做一个读取操作
                        // 将读取到的文件存到ArrayList中
                        ArrayList list = (ArrayList<Books>) (ois.readObject()); // 里面是读取到的数据
                        list.add(b); // 添加个体到集合中去

                        // 将读取到的的文件添加我们要加入的数据
                        // 需要使用流来处理文件(程序 -> 文件)
                        FileOutputStream fos = new FileOutputStream(f);
                        ObjectOutputStream oos = new ObjectOutputStream(fos);

                        // 借助输出流,执行一个写出的操作
                        oos.writeObject(list);
                        // 关闭流
                        oos.close();
                    }
                } else {
                    // 如果不存在文件,说明可以创建一条数据
                    // 文件不存在,创建新文件并添加书籍
                    ArrayList<Books> list = new ArrayList<>(); // 空集合
                    list.add(b);
                    // 将集合对象写出到硬盘盘符文件中
                    // 文件
//                    File file = new File(FilePath);
                    // 需要使用流来处理文件(程序 -> 文件)
                    FileOutputStream fos = new FileOutputStream(f);
                    ObjectOutputStream oos = new ObjectOutputStream(fos);

                    System.out.println("新增数据"+list);
                    // 借助输出流,执行一个写出的操作
                    oos.writeObject(list);
                    // 关闭流
                    oos.close();
                }
            }
            if(choice == 3){
                System.out.println("<下架书籍>");
                // 录入要下架的书籍的编号
                System.out.println("请录入你要下架的书籍的编号:");
                int deNo = sc.nextInt(); // 获取键盘输入内容
                ArrayList list;
                File f = new File(FilePath);
                if(f.exists()){
                    // 如果存在文件,先读取文件内容,再删除对应序号(需要判断是否存在对应编号的数据)
                    FileInputStream fis = new FileInputStream(f);
                    try (ObjectInputStream ois = new ObjectInputStream(fis)) {
                        list =  (ArrayList<Books>) (ois.readObject()); // 拿到文件中对应的数据
                        boolean found = false; // 标志变量,表示是否找到对应的书籍编号
                        // 下架编号对应的书籍
                        for (int i = 0; i < list.size(); i++) {
                            Books b = (Books)(list.get(i));
                            if(b.getbNo() == deNo){ // 如果遍历的书籍编号和录入的要删除的书籍编号一致,那么从集合中移除该书籍即可
                                list.remove(b);
                                System.out.println("书籍下架成功");

                                // 写入文件
                                FileOutputStream fos = new FileOutputStream(f);
                                ObjectOutputStream oos = new ObjectOutputStream(fos);

                                // 借助输出流,执行一个写出的操作
                                oos.writeObject(list);
                                // 关闭流
                                oos.close();
                                found = true;
                                break; // 如果下架成功,就停止遍历,不需要后续的遍历
                            }
                        }
                        if(!found){
                            System.out.println("输入的书籍编号不存在,请重新输入");
                        }
                    }
                } else {
                    System.out.println("暂无书籍,请先创建书籍");
                }
            }
            if(choice == 4){
                System.out.println("<退出应用>");
                break; // 停止正在执行的循环
            }
        }
    }
}

2.3 关键修改点

Books:序列化操作

 Test:

     1、展示书籍操作中,新增读取文件操作,并且将文件内容赋值给List进行遍历读取  

        2、上新书籍中,新增判断文件是否存在逻辑。

        如果存在文件:先读取文件内容,再赋值,再新增数据,再输出到文件;

        如果不存在文件:直接赋值,再输出到文件

         3、下架书籍中,判断文件是否存在

        如果文件存在,先读取文件,然后赋值,然后遍历数组,匹配输入值,删掉对应的值,最后将数组输出到文件中,

        如果文件不存在,提示用户“请创建书籍信息”。

三、线程

 3.1 程序、线程、进程

        程序:是为完成特定任务、用某种语言编写的一组指定的集合,和一段静态的代码。(程序是静态的)

        进程:是程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。

        线程:进程可进一步细化为线程,是一个进程内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程的。 

进程是操作系统进行资源分配的基本单位。

线程是操作系统调度执行的基本单位。

3.2 创建线程

创建线程的三种方式:

  1. 方式1:继承Thread类
  2. 方式2:实现Runnable类
  3. 方式3:实现Callable类

四、网络编程

4.1 网络编程、IP、端口号

网络编程

        把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。设备之间在网络中进行数据地传输,发送/接收数据。

IP地址

        用来标志网络中的一个通信实体的地址。通信实体可以是计算机、路由器等。

端口号

        IP地址用来标志一台计算机,但是一台计算机上可能提供多种应用程序,使用端口来区分这些程序。

4.2 网络通信协议

        计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等指定标准。

        由于节点间联系复杂,制定协议时,把复杂成份分解成一些简单的成份,再将他们复合起来。最常用的复合方式时层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。

4.3 网络同行协议的分层

        名义上标准:ISO/OSI参考模型

        事实上标准:TCP/IP协议栈(Internet使用的协议)

4.4 Socket套接字

        我们开发的网络应用程序位于应用层,TCP和UDP属于传输协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。

        套接字就像传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或接收远程发来的数据,而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心他如何传输,这属于网络其他层次的工作。

        Socket实际是传输层共给应用层的编程接口。传输层则在网络层的基础上提供进程到进程间的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输之间的桥梁。

4.5 代码实战:套接字实现双向通信

TestClient对象:客户端

package com.study.java17;

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

public class TestClient { // 客户端
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动🚀🚀🚀");
        // 套接字:指定服务器的IP(换成自己电脑的IP)和端口号
        Socket so = new Socket("192.168.137.1",8888);
        // 对于程序员来说,感受利用输出流在传递数据
        OutputStream os = so.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os); // 数据流
        // 传送数据
        dos.writeUTF("Hello World");

        // 对服务器返回的数据做处理
        InputStream is = so.getInputStream();
        DataInputStream dis = new DataInputStream(is);
        String s = dis.readUTF();
        System.out.println("服务器对客户端说:" + s);
        // 流、网络资源关闭
        dos.close();
        os.close();
        so.close();
    }

}

TestSeaver对象:服务器端

package com.study.java17;

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

public class TestSeaver {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器端启动!");
        // 接收数据
        // 套接字
        ServerSocket ss = new ServerSocket(8888);
        // 等待客户端发送数据
        Socket s = ss.accept();
        // 服务器端感受到的输入流
        InputStream is = s.getInputStream(); // 接收流
        DataInputStream dis = new DataInputStream(is); // 通过数据流处理接受流

        // 接收客户端发送的数据
        String str = dis.readUTF();
        System.out.println("客户端说" + str);

        // 向客户端发送数据
        OutputStream os = s.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeUTF("你好客户端,我接受到你的信息了");
        // 流、网络资源关闭
        dos.close();
        os.close();
        dis.close();
        is.close();
        ss.close();
    }
}

五、XML

5.1 XML是什么

        XML是可扩展标记语言(EXtensible Markup Language),是一种用于描述数据结构和传输数据的标记语言,具有自描述性、可扩展性和跨平台兼容性

5.2 XML的作用是什么

        XML是不作为的,XML不会做任何事情。XML被设计用来结构化、存储以及传输信息。它仅仅是纯文本而已。它仅仅将信息包装在XML标签中。我们需要编写软件或者程序,才能传送、接收和显示这个文档。

5.3 XML的定义

(1)必须有声明语句

XML声明是XML文档的第一句,其格式如下:

<?xml version="1.0" encoding="utf-8" ?>

(2)XML文档有且只有一个根元素

        良好格式的XML文档必须有一个根元素,就是紧接着声明后面简历的第一个元素,其他元素都是这个根元素的子元素,根元素完全包括文档中其他所有元素。

(3)注意大小写

        在XML文档中,大小写是有区别的。“A”和“a”代表不同的标记。

(4)所有的标记必须有相应的结束标记

        所有标记必须成对出现,有一个开始标记,就必须有一个结束标记,否则将被视为错误。

(5)属性值使用引号

        所有属性必须加引号(可以是单引号,也可以是双引号,建议使用双引号),否则将被视为错误

(6)XML中可以加入注释

        注释格式:<!--  -->

下面是一个XML示例文件:

<?xml version="1.0" encoding="utf-8" ?>
<!--
   version:版本号
   encoding:文档编码
   students:根标签
   student:子标签
   name,age,sex,score:student子标签的子标签
   id:标签的属性
 -->
<students>
    <student id="1">
        <name>张三</name>
        <age>18</age>
        <sex>男</sex>
        <score>90</score>
    </student>
    <student id="2">
        <name>李四</name>
        <age>52</age>
        <sex>男</sex>
        <score>60</score>
    </student>
</students>

5.4 解析XML

(1)常见的解析方式

        开发中比较常见的解析方式有三种,如下:

1.DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。

        (1)优点:元素与元素之间保留结构关系,故可以进行增删改查操作。

        (2)缺点:XML文档过大,可能出现内存溢出显现。

2.SAX: 是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。 并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)

         (1)优点:处理速度快,可以处理大文件

         (2)缺点:只能读,逐行后将释放资源。

3.PULL:Android内置的XML解析方式,类似SAX。(了解)

(2)DOM解析方式

        DOM解析:需要使用工具dom4j

        代码:就是一个jar包

(3)依赖使用步骤:

        ①下载dom4j的jar包依赖(这里后续我会将这个jar上传到资源中,可以方便使用)

        ②在src同级目录创建lib文件夹,将jar包放进去

        ③选中jar包,右键-添加为库,这样这个依赖才能在项目中使用

(4)代码解析XML
package com.study.java18;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) throws DocumentException {
        // 读取XML
        // 1.创建一个XML解析器对象:(就是一个流)
        SAXReader sr = new SAXReader();
        // 2.读取XML文件,返回Document对象出来
        Document dom = sr.read(new File("firstModule/src/students.xml")); // 文件地址改成自己文件的路径
        // 3.获取根节点:(根节点只有一个)
        Element studentsEle = dom.getRootElement();
        // 4.获取根节点下的多个子节点
        Iterator<Element> it1 = studentsEle.elementIterator();
        while (it1.hasNext()) {
            // 4.1 获取到子节点对象
            Element studentEle = it1.next();
            // 4.2 获取子节点的属性
            List<Attribute> atts = studentEle.attributes();
            for (Attribute att : atts) {
                System.out.println("该节点的属性:"+att.getName() + " ----- " + att.getText());
            }
            // 4.3 获取到子节点的子节点
            Iterator<Element> it2 = studentEle.elementIterator();
            while (it2.hasNext()) {
                Element eles = it2.next();
                System.out.println("<节点>"+eles.getName() + " ----- " + eles.getText());
            }
            // 5. 每行输出后加一个换行
            System.out.println();
        }

    }
}
(5)代码解释

 六、注解

6.1 注解的概念

(1)什么是注解?

        注解其实就是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。

(2)注解的使用

        使用注解时要在其前面增加@符号,并把该注解当成一个修饰符使用。用于修饰它支持的程序元素(包、类、构造器、方法、成员变量、参数、局部变量的声明)。

(3)注解的重要性

        在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/ArIdroid中注解占据了重要的角色,例如用来配置应用程序中的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。未来的开发模式都是基于注解的。一定程度上可以说:框架=注解+反射+设计模式

6.2 注解的使用示例

  1. 标识类的作者@author
  2. 指定类的版本@version
  3. 说明方法的参数@param
  4. 说明方法的返回值类型@return
  5. 限定重写的方法@Override

@author、@version、@param、@return

package com.study.java19;

/**
 * @author 小张
 * @version 1.0
 */
public class Test {
    /**
     *
     * @param num1 求和的第一个变量
     * @param num2 求和的第二个变量
     * @return 当前方法返回值为int类型
     */
    public int add(int num1,int num2){
        return num1+num2;
    }
}

 @Override标识重写方法

package com.study.java19;

public class Student extends Person {
    @Override
    public void eat() {
        System.out.println("这是对父类的方法的重写");
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到