java进阶

本文详细介绍了Java中的IO流,包括File类的基本操作、字节流与字符流、缓冲流、转换流、对象流以及打印流。此外,还讲解了扫描器Scanner的使用。接着,探讨了NIO(非阻塞I/O)的特点和优势,如通道、缓冲区和选择器。最后,简述了Java多线程的概念,包括线程的生命周期、同步机制和死锁的避免。
摘要由CSDN通过智能技术生成

File类

  1. 常用方法

    getName();	isDirectory(); isFile();	exists();	listFiles(); createNewFile();	mkdir();	mkdirs();
    getPath();	getAbsolutePath();	delete();	deleteOnExit();	renameTo();	getParentFile();
    

IO流

  1. 概述:为了在程序中做数据存储的持久化,按流向可以分为输入流跟输出流,按传输的数据单位可以分为字节流与字符流。

  2. 四大基本流:字节输入流,字节输出流,字符输入流,字符输出流

  3. 无论哪种流,一定要关闭资源,否则会造成阻塞,磁盘文件会被占用,不能删除也不能修改。

  4. 字节流与字符流的选择

    • 二进制文本,图片,视频,音频必须使用字节流
    • 纯文本文件使用字符流
    • 不清楚文件类型使用字节流
  5. flush(刷新操作)

    1. 原因:为了提高写入与读取效率,设置一个缓冲区,当缓冲区的数据达到特定值之后,再将其写入磁盘
    2. 意义
      • 可以大大提高cpu的使用率。
      • 可以通过操作缓冲区回滚数据。
    3. 操作系统使用-1代表磁盘文件结束的标志。
    4. IO是最影响程序性能的,缓冲区设置容量的整数倍,可以有效提高IO性能。
  6. try()catch代码块自动释放文件资源

    try(FileInputStream in = new FileInputStream(file);//需要关闭的流卸载try代码块之中。
                    FileOutputStream out = new FileOutputStream(file2)){
        
    	 byte[] bytes = new byte[1024];
        int len;
        while ((len = in.read(bytes)) != -1) {
            out.write(bytes, 0, len);
        }   
    }catch(IOException e){
        e.printStackTrace();
    }
    

缓冲流

  1. 其实就是个包装流,提供缓冲区,提高IO效率
  2. 字节缓冲流
    1. 输入:BufferedInputStream
    2. 输出:BufferedOutputStream
  3. 字符缓冲流
    1. 输入:BufferedReader
    2. 输出:BufferedWriter

转换流

  1. 定义:把字节流转换成字符流

    1. InputStreamReader
    2. OutputStreamWriter
  2. InputStreamReader isr = new InputStreamReader(fis, "utf-8")
    

合并流

  1. 把多个输入流,合并为一个流对象

  2. SequenceInputStream sis = new SequenceInputStream(fis, fis2);
    

对象流

  1. 序列化以及反序列化
  2. 序列化:把内存中的java对象存储到磁盘中的过程,或者给其他的网络节点使用
  3. 反序列化:把磁盘文件的数据恢复成内存中的java对象
  4. 为什么要使用序列化
    • 数据的共享需要实现序列化(redis)
    • 服务的钝化:把不常用的对象存储到磁盘,来节省内存的开销
    • 使用json传输的时候
  5. 要求:必须要实现序列化接口
  6. 常见问题
    • 没有实现序列化接口
    • 序列号版本对不上,需要手动引入版本号,避免属性改变,不兼容问题

打印流

  1. 用来打印数据,打印流只能是输出流
    • PrintStream
    • PrintWriter
  2. 拓展点:打印流中的格式化输出:printf();

扫描器类Scanner

  1. java.util.Scanner:扫描器类,做输入操作的

    hasNextXXXX();//判断有没有这个类型的数据
    xxx nextXXX();//得到下个类型的数据
    

数据流

  1. 提供了可以读写任何数据的方法

    • DataInputStream:提供了readXXX的方法
    • DataOutputStream:提供了writeXXX的方法
  2. 操作

    1. 读取

      DataInputStream dis = new DataInputStream(new FileInputStream(file));
              System.out.println(dis.readInt());
              System.out.println(dis.readUTF());
              dis.close();
      
    2. 写入

      DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
              dos.writeInt(123342234);
              dos.writeUTF("上云老师教的真好呀!");
              dos.close();
      

Properties

  1. 配置文件,资源文件以.properties作为拓展名,使用key_value模式存储数据
  2. 以后会使用ClassLoader来读取(反射的时候补充)

NIO

  1. jdk1.4之后提供的,可以把一块磁盘文件映射到内存中去,java.nio下面的

  2. 应用领域:一般应用在云服务器中

  3. jdk1.7也提供了一个工具包, java.nio.Files

    long length = Files.copy(new FileInputStream("E:\\ideaProject\\java核心技术\\基础\\src\\day08\\NIO\\a.txt"),
                    Paths.get("E:\\ideaProject\\java核心技术\\基础\\src\\day08\\NIO\\b.txt"));
    
            System.out.println(length);//length:文件的字节数,
    
  4. 与io有何不同

    • Channels Buffers(通道和缓存), 使用通道和缓冲区进行操作,性能更快
    • 异步的,线程从通道读取数据到缓冲区的时候,线程还可以做其他事情
    • Selectors(选择器):可以监听多个通道的事件

反射

类加载机制

  1. jvm和类的关系

    • 运行带有main方法的类(主方法),会启动JVM进程,并加载类的字节码文件,jvm所有线程以及变量都处于同一进程中。
    • JVM何时退出:
      • 程序正常运行结束
      • 出现异常(没有捕获)
      • System.exit();方法
      • 强制杀死进程
    • JVM进程一旦结束,进程中内存的数据会丢失
  2. 类加载机制

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-INx6Bwyk-1656170873053)(C:\Users\cc\AppData\Roaming\Typora\typora-user-images\image-20220619182651831.png)]

  3. 类加载初始化具体步骤(加载,连接,初始化)

    • 类的加载
      • 类加载器(ClassLoader, 由JVM提供,或者自定义): 将字节码文件(class文件)加载进内存,并且创建一个class对象(java.lang.class)
    • 类的连接:(类加载进内存后,生成class对象,把类的二进制数据合并到JRE中)
      • 验证:检测被加载的类是否有正确的内部结构
      • 准备:负责为类的static变量分配内存空间,设置默认值(并不是初始化操作)
      • 解析:把类的二进制数据中的符号引用替换为直接引用(深入分析JVM)
    • 类的初始化(jvm负责对类进行初始化,主要对static变量进行初始化)
      • 如果该类未被加载和连接,则程序先加载并连接该类,类还可以动态加载(groovy)
      • 如果该类的父类未被初始化,则优先初始化父类
      • 如果该类有初始化语句(静态代码块),系统依次执行这些语句

反射的概述

  1. 问题:Object obj = new String(“abc”);
    • 需求:如何调用String中的length方法
      • 使用强制类型转换
  2. Class对象(表示所有的类)
    1. Constructor:表示所有的构造器
    2. Method:表示所有的方法
    3. Field:表示所有的字段
    4. 通过反射,得到类的元数据信息(构造器,方法,字段,内部类)
  3. 当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,

Class类和Class实例

  1. Class类表示所有的类

  2. Class实例表示一份份的字节码(类, 接口,注释)

  3. String类如何表示:

    • String Class<java.lang.String>
    • HashMap Class<java.util.HashMap>
  4. 获取类的实例

    • 使用类名.class:String.class
    • 使用Class的静态方法:Class.forName(“类的全类名”)
    • 使用对象.getClass()方法:对象.getClass();
  5. 同一个JVM中,只存在一个类的一份字节码和一个字节码对象

  6. 基本类型的字节码的表示

    •         Class<Integer> integerClass = int.class;//int类型的类实例,jvm提前内置了
              Class<Integer> type = Integer.TYPE;//包装类型提供了一个type常量,也是可以获取基本类型的类实例
      
    • 证明Integer和int不是同一种类型

      System.out.println(Integer.class == int.class);
      System.out.println(Integer.class == Integer.TYPE);
      

反射操作构造器

  1. Class去操作Constructor类

  2. 常用的方法

//获取构造器对象
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取全部构造器对象
public Constructor<?>[] getConstructors()
//获取私有的构造器
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

  1. 获取到的私有构造器不能直接创建对象,需要setAccessible(true);

反射操作对象的方法

  1. 获取静态方法

    //获取静态对象,调用方法不需要传入对象
            Method add = c.getMethod("add", Student.class);
            add.invoke(null, new Student());
    

使用类加载器加载Properties

  1. 从当前项目的根目录下开始找(使用文件输入流)

    Properties pro = new Properties();
    pro.load(new FileInputStream("基础/src/day08/propertiesDemo/a.properties"));
    
  2. 从src目录下开始找(使用当前线程的类就在其)

    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    InputStream is = contextClassLoader.getResourceAsStream("day08/propertiesDemo/a.properties");
    
  3. 从当前类的目录下开始找(使用当前类的类加载器)

    InputStream is = PropertiesDemo3.class.getResourceAsStream("a.properties");
    

多线程

并行和并发

  1. 并行:多件事情在同一时刻同时发生
  2. 并发:多件事情在同一时间段同时发生
  3. 单核cpu:一个时刻只能执行一个程序,多个程序可以交替执行。
  4. 线程调度:多个线程以某个顺序执行

进程和线程

  1. 进程:内存中的正在运行的程序,在内存中分配一块空间(独立的),一个应用程序可以同时启用多个进程,至少一个线程
  2. 线程:进程中的执行单元,一个进程可以有多个线程,栈空间独立,堆空间共享
  3. 多线程的优势:因为堆空间共享,所以内存空间开销较少。
  4. 多线程具体运行哪个线程,取决于cpu的调度
  5. java中至少有几个线程
    • 主线程
    • GC垃圾回收线程
  6. 线程调度:JVM采用抢占式的线程调度,没有分时执行的概念,谁抢到谁执行
  7. 应用:抢票,游戏,多线程下载

多线程的实现方式

  1. 继承Thread类

    MyThread myThread = new MyThread();
    
  2. 实现Runnable接口

    Thread thread = new Thread(new MyRunable());
    

线程的调度

  1. public static native void sleep(long millis)//线程睡眠 
    public final synchronized void join(long millis)//阻塞调用join方法的线程,让我执行完毕,再恢复
    
  2. 守护线程:GC的使用

    • 功能:为其它线程服务
    • 特点:主线程结束,守护线程就结束
    • 创建线程:默认是前台线程
    • 获取守护线程:Thread.setDaemon(true),在start方法执行之前设置
  3. 获取当前线程及其名称

    Thread currentThread = Thread.currentThread();
    System.out.println(currentThread.getName());
    
  4. 线程设置优先级

    • 注意:优先级高,只是获取执行机会更大(取决于cpu调度)
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmNYWWNE-1656170873054)(C:\Users\cc\AppData\Roaming\Typora\typora-user-images\image-20220619230439833.png)]
  5. 线程的让位:yield();

    • 当前线程向cpu调度器表示愿意让出cpu资源(但是调度器可以忽略)
    • sleep和yield的区别:
      • 都能使得当前处于运行状态的线程暂时放弃cpu,把机会留给其他线程
      • sleep不考虑其他线程的优先级,给其他线程运行机会,yield只会给同等优先级或优先级比自己高的运行机会
      • 调用sleep方法后,线程进入等待状态,调用yield方法后线程进入就绪状态

线程的同步操作

  1. 同步方法(synchronized修饰方法)

  2. 同步代码块

    • synchronized(对象锁)
    • synchronized(class对象锁)
  3. 锁对象

    Lock lock = new ReentrantLock();
    lock.lock();
    lock.unlock();
    

生产者与消费者案例分析

  1. 生产者(多个线程)与消费者(多个线程)共享数据
  2. 解决生产者和消费者的数据紊乱问题
    • 只要保证生产产品的过程中,不允许消费者操作产品
    • 同步方法/同步代码块/锁
  3. 应该先生产一个数据,然后再消费一个数据
    • 生产者生产完一个数据,唤醒消费者,然后进入等待
    • 消费者醒了后,开始消费,然后唤醒生产者,进入等待
    • 使用等待唤醒机制

等待和唤醒机制

  1. 线程通信(wait)和(notify)方法介绍

    • wait(); 执行该方法的线程对象要释放同步锁,并把该线程放到等待池中去等待
    • notify(); 执行该方法的线程对象唤醒等待池的线程,并将其添加到锁池中去等待
    • notifyAll(); 执行该方法的线程对象唤醒等待池中的所有线程,把这些线程都转移到锁池中去等待
    • 注意:这些方法只能被同步监听对象锁(同步锁)调用
  2. 同步监听锁对象(同步锁):多个线程有公共对象使用时,多个线程之间才会出现互斥现象,共同的对象就叫做同步监听锁对象

  3. 同步锁池:同步锁必须选择多个线程共有的资源对象,当生产者生产数据时(先获取锁),生产者没生产出来的时候,消费者只能在锁池等待,当生产者生产完后,释放同步锁,消费者可以抢锁来进行消费。

  4. Lock和Condition接口

    • Lock机制根本没有同步锁,也没有释放锁的逻辑

    • 使用Condition接口对象中的await, signal, signalAll方法取代Object中的wait, notify, notifyAll

      private Lock lock = new ReentrantLock();
      private Condition condition = lock.newCondition();
      
  5. 建议使用第二种方式:更加灵活

死锁

  1. 多线程通信中,很容易出现死锁现象,死锁是不能解决的,只能避免
    • 如P线程等待C线程持有的锁,C线程等待P线程持有的锁,JVM不检测也不能避免这种情况
    • Thread类中的**suspend()和resume()**方法很容易导致死锁,不要去使用。
      • suspend():是正在运行的线程暂时放弃cpu,但是不释放锁。
  2. 如何避免死锁:当线程A、B、C去访问共享资源的时候,保证每一个线程都按照相同的顺序访问。
    • 上锁之后记得释放锁,如果有finally代码块,将释放锁的操作放到finally代码块中

线程的生命周期

  1. 线程状态

    • NEW:还未开启的线程状态
    • RUNNABLE:正在运行中的线程状态,也可能在等待其他资源比如(处理器)
      • ready:等待cpu的调度
      • running:正在运行中
    • TIMED_WAITING(计时等待):调用Tread.sleep(long),Object.wait(long), Thread.join(long)会进入该状态
    • WAITING(等待的状态):只能被其他线程唤醒
      • 等待中的线程,由于调用了Object的wait()方法,或者Thread的join()方法
      • 运行中的线程调用了wait()方法,此时JVM会将其放在等待池中等待唤醒
    • BLOCKED(阻塞的状态):运行中的线程,由于某些原因暂时放弃了cpu,此时jvm不会为线程分配cpu
      • 线程处于运行状态,但没有获取到同步锁时,JVM会把该线程放到同步锁对象的锁池中去,该线程阻塞
      • 线程运行中发出IO请求,JVM会将该线程阻塞
    • TERMINATED(死亡的状态):
      • 线程结束了
      • 出现异常
    image-20220621102320557

定时器与线程组

  1. 定时器的使用

    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("================");
        }
    }, 10000L, 2000L);
    

线程组

  1. 可以对线程集中管理,ThreadGroup
  2. 用户创建线程时,可以指定线程组

注解

  1. java注解:jdk1.5开始引入,也称为java标记,可以通过反射获取标记,编译器在编译java文件时,也会把java注解编译到字节码文件中去
  2. 注解的使用场景

JAVA8特性

  1. Lambda表达式:允许一个函数作为参数传递到方法中去。

  2. stream(数据源的元素队列,支持聚合操作, 想象成流)

    • 数据源:集合,数组
    • 聚合操作:filter(过滤), map(映射),limit(截取指定数量),reduce,find, match, sorted(排序), forEach(循环)
  3. 接口中可以提供静态方法和默认方法

  4. 新的dataAPI

    • 旧版存在问题

      • 设计较差(Java.util, java.sql包都有
      • 非线程安全的
      • 处理时区比较麻烦
    • 新版提供

      • Local(本地):简化了事件处理,没有了时区的问题

      • Zoned(时区):指定时区处理时间

        LocalDateTime time = LocalDateTime.now();
        System.out.println(time);
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime parse = LocalDateTime.parse("1999-07-20 12:00:45", formatter);
        System.out.println(parse);
        
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId);
        

网络编程

IP

  1. 获取ip地址

    InetAddress ip = InetAddress.getByName("DESKTOP-QQ9QB67");
    System.out.println(ip.getHostName());
    System.out.println(ip.getHostAddress());
    //判断是否可以连接
    System.out.println(ip.isReachable(1000));
    

端口

  1. 设备和外部设备通信交流的出口
    • 常用端口:80, 8080, 3306, 6379
  2. 注意:同一台电脑不能同时出现同一个端口,否则端口冲突

协议

  1. 通信双方必须遵守的规则
  2. http:超文本传输协议(免费)
  3. https:使用安全的套接字传输的超文本协议(收费)
  4. ftp:文件传输协议
  5. file:本机电脑或者网上分享的协议

URI与URL

  1. uri:统一资源标识符
    • 表示某一个互联网资源的字符串:包含主机名,端口,标识符
  2. url:统一资源定位符
    • 是互联网的标准资源地址

传输层协议

  1. TCP:面向连接(三次握手),传输可靠(保证数据的正确性和数据顺序正确性),建立连接开销大,用来传输数据量大的,速度慢

  2. UDP:面向非连接,传输不可靠,用于传输少量数据(数据报包模式)、速度快,用于发送端和接收端

  3. 实操TCP

    //建立一个服务器套接字
    ServerSocket server = new ServerSocket(9999);
    System.out.println("服务器准备就绪");
    //与客户端建立连接
    Socket accept = server.accept();
    OutputStream outputStream = accept.getOutputStream();
    PrintStream ps = new PrintStream(outputStream);
    ps.print("ip地址是:"+accept.getInetAddress());
    ps.print("连接服务器成功!");
    ps.close();
    server.close();
    
    Socket socket = new Socket("127.0.0.1", 9999);
    InputStream is = socket.getInputStream();
    Scanner scanner = new Scanner(is);
    while (scanner.hasNext()){
        System.out.println(scanner.next());
    }
    scanner.close();
    socket.close();
    
  4. 实操UDP

    String data = "我是发送者";
    //创建发送端对象
    DatagramSocket socket = new DatagramSocket(9999);
    DatagramPacket packet = new DatagramPacket(data.getBytes(), 
            data.getBytes().length, InetAddress.getLocalHost(), 1010);
    socket.send(packet);
    socket.close();
    
    //建立接收端对象
    DatagramSocket socket = new DatagramSocket(1010);
    byte[] buf = new byte[1024];
    DatagramPacket pack = new DatagramPacket(buf, 1024);
    socket.receive(pack);
    String s = new String(buf, 0, pack.getLength());
    System.out.println(s);
    socket.close();
    
    String data = "我是发送者";
    //创建发送端对象
    DatagramSocket socket = new DatagramSocket(9999);
    DatagramPacket packet = new DatagramPacket(data.getBytes(), 
            data.getBytes().length, InetAddress.getLocalHost(), 1010);
    socket.send(packet);
    socket.close();
    
    //建立接收端对象
    DatagramSocket socket = new DatagramSocket(1010);
    byte[] buf = new byte[1024];
    DatagramPacket pack = new DatagramPacket(buf, 1024);
    socket.receive(pack);
    String s = new String(buf, 0, pack.getLength());
    System.out.println(s);
    socket.close();
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值