一篇快速搞懂 JavaSE 高级特性(代码块,注解,枚举,异常处理,多线程,集合框架,泛型,反射,IO ......)

JavaSE 高级

一、面向对象(高级)

1、单例模式(Singleton)

单例模式(饿汉式):“立即加载”,随着类的加载,当前的唯一实例就创建了,是线程安全的。

package oopPlus.atguigu02.singleton;

/*
* 单例模式(饿汉式):“立即加载”,随着类的加载,当前的唯一实例就创建了
* */
public class BankTest {
    public static void main(String[] args) {
        Bank bank = Bank.getInstance();
    }
}

class Bank {
    //构造器私有化
    private Bank() {
        
    }
    
    //类内部创建类的实例
    private static Bank instance = new Bank();
    
    //get方法获取当前类的实例
    public static Bank getInstance() {
        return instance;
    }
} 


单例模式(懒汉式):“延迟加载”,在需要使用的时候,进行创建,线程不安全。

package oopPlus.atguigu02.singleton;

/*
 * 单例模式(懒汉式):“延迟加载”,在需要使用的时候,进行创建
 * */
public class GirlFriendTest {
    public static void main(String[] args) {
        GirlFriend girlFriend = GirlFriend.getInstance();
    }
}

class GirlFriend {
    private GirlFriend() {

    }

    private static GirlFriend girlFriend = null;

    public static GirlFriend getInstance() {
        if (girlFriend == null) girlFriend = new GirlFriend();

        return girlFriend;
    }
}
	

2、代码块

1)静态代码块

  • 随着类的加载而执行

  • 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次。

  • 作用:初始化类的信息

  • 内部可以声明变量、调用属性或方法、编写输出语句等操作。

  • 静态代码块的执行要先于非静态代码块的执行。

  • 如果声明有多个静态代码块,则按照声明的先后顺序执行。

  • 静态代码块内部只能调用静态的结构(静态属性、方法),不能调用非静态的结构(非静态属性、方法)。

static {
	...
}

2)非静态代码块

  • 随着对象的创建而执行。
  • 每当创建当前类的一个实例,就会执行一次非静态代码块。
  • 作用:初始化对象的信息
  • 内部可以声明变量、调用属性或方法、编写输出语句等操作。
  • 如果声明有多个非静态代码块,则按照声明的先后顺序执行。
  • 非静态代码块内部可以调用静态的结构(静态属性、方法),也可调用非静态的结构(非静态属性、方法)。
{
	...
}

对象的实例变量可以赋值的位置及先后顺序

① 默认初始化

② 显式初始化或代码块中初始化

③ 构造器中初始化

④ 通过"对象.属性"或"对象.方法"的方式进行赋值

3、关键字 final

final 可以用来修饰的结构:类、方法、变量

  • final 修饰类:表示此类不能被继承。如:String、StringBuffer、StringBuilder。

  • final 修饰方法:表示此方法不能被重写。如:Object中的getClass()

  • final 修饰变量:可以修饰成员变量也能修饰局部变量,将其变为常量。

    • 修饰成员变量:哪些位置可赋值?
      • 显示赋值
      • 代码块中赋值
      • 构造器中赋值
    • 修饰局部变量:一旦赋值不可修改
      • 方法内声明的局部变量:在调用局部变量前一定要赋值。
      • 方法的形参:在调用方法时,给形参赋值。
  • final 和 static 搭配修饰的成员变量:全局常量。如:Math.PI

4、抽象类与抽象方法(abstract)

在这里插入图片描述

abstract 修饰类:

  • 此类为抽象类,抽象类不能实例化。
  • 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接调用到父类的构造器。
  • 抽象类中可以没有抽象方法,但抽象方法所在的类一定是抽象类。

abstract 修饰方法:

  • 此方法为抽象方法,抽象方法只有方法的声明,没有方法体。
  • 任何继承抽象类的子类都必须实现抽象类中的所有抽象方法,否则该子类也必须声明为抽象类。
  • 抽象方法提供了一种多态性的机制,使得不同的子类可以以自己的方式实现相同的抽象方法。

在这里插入图片描述

5、模板方法设计模式(TemplateMethod)

在这里插入图片描述

public class TemplateMethodTest {
    public static void main(String[] args) {
        TemplateMethod t1 = new DrawMoney();
        t1.process();
        
        TemplateMethod t2 = new ManagerMoney();
        t2.process();
    }
}

abstract class TemplateMethod {
    abstract void transact();
    
    public void takeNumber() {
        System.out.println("排队");
    }
    
    public void evaluate() {
        System.out.println("反馈评价");
    }
    
    public final void process() {
        this.takeNumber();
        this.transact();
        this.evaluate();
    }
}

class DrawMoney extends TemplateMethod {

    @Override
    void transact() {
        System.out.println("存钱!!!!!");
    }
    
}

class ManagerMoney extends TemplateMethod {
    @Override
    void transact() {
        System.out.println("理财!!!");
    }
}

6、接口(interface)

接口的本质是契约、标准、规范
在这里插入图片描述

接口内部结构:

  • 可以声明:

    • 属性:用 public static final 修饰

      public interface MyInterface {
          //public static final 可省略
          public static final int MAX_VALUE = 100;
      }
      
    • 方法:声明抽象方法,修饰为 public abstract

      public interface MyInterface {
          //public abstract 可省略
          public abstract void abstractMethod();
      }
      
  • 不可以声明:构造器、代码块。

接口与类的实现:class A extends SuperA implements B, C {}


  • 类可以实现多个接口。

  • 类针对于接口的多实现,一定程度上弥补了类的单继承的局限性。

  • 类必须将实现的接口中的所有方法都重写,才能实例化。否则,此实现类必须声明为抽象类。

  • 接口于接口之间可以多继承。

  • 接口的多态性接口名 变量名 = new 实现类对象;

    Flyable f = new Bullet();
    f.fly();
    

接口和抽象类之间的对比:
在这里插入图片描述

JDK8 和 JDK9 的新特性

  • **静态方法(Static Methods):**从Java 8开始,接口还支持静态方法。静态方法以关键字 static 开头,可以通过接口名称直接调用,不需要实例化接口。

    public interface MyInterface {
        static void staticMethod() {
            System.out.println("Static method implementation");
        }
    }
    
  • 默认方法(Default Methods): 从Java 8开始,接口支持默认方法,即在接口中可以包含有具体实现的方法。默认方法以关键字 default 开头,并且可以在接口中提供默认的方法实现。

    public interface MyInterface {
        void abstractMethod();
        
        default void defaultMethod() {
            System.out.println("Default method implementation");
        }
    }
    
  • 私有方法(Private Methods): 从Java 9开始,接口支持私有方法,这些方法只能在接口内部使用。私有方法以关键字 private 开头。

    public interface MyInterface {
        default void defaultMethod() {
            privateMethod();
        }
        
        private void privateMethod() {
            System.out.println("Private method implementation");
        }
    }
    

7、内部类

在这里插入图片描述

  • 成员内部类(Member Inner Class): 成员内部类是定义在类内部的普通类。它与外部类的实例关联,可以访问外部类的成员,包括私有成员。

    public class OuterClass {
        private int outerVar;
    
        public class InnerClass {
            public void innerMethod() {
                System.out.println("Accessing outerVar from innerMethod: " + outerVar);
            }
        }
    }
    
  • 静态成员内部类(Static Nested Class): 静态成员内部类是定义在类内部的静态类。与成员内部类不同,它不依赖于外部类的实例,可以直接通过外部类名访问。

    public class OuterClass {
        private static int outerStaticVar;
    
        public static class StaticInnerClass {
            public void staticInnerMethod() {
                System.out.println("Accessing outerStaticVar from staticInnerMethod: " + outerStaticVar);
            }
        }
    }
    
  • 局部内部类(Local Inner Class): 局部内部类是定义在方法内部的类,它只在该方法内可见。通常用于解决某个特定问题。

    public class OuterClass {
        public void outerMethod() {
            class LocalInnerClass {
                public void localInnerMethod() {
                    System.out.println("Inside localInnerMethod");
                }
            }
    
            LocalInnerClass localInner = new LocalInnerClass();
            localInner.localInnerMethod();
        }
    }
    
  • 匿名内部类(Anonymous Inner Class): 匿名内部类是没有显式定义类名的内部类,通常用于实现接口或继承抽象类。它可以在创建对象的同时提供实现。

    public interface MyInterface {
        void myMethod();
    }
    
    public class OuterClass {
        public MyInterface createAnonymousClass() {
            return new MyInterface() {
                @Override
                public void myMethod() {
                    System.out.println("Anonymous class implementation");
                }
            };
        }
    }
    

8、枚举类 Enum

在这里插入图片描述

JDK 5.0 之前自定义枚举类的方式(了解):

public class SeasonTest {
    public static void main(String[] args) {
        
        System.out.println(Season.SPRING);
        System.out.println(Season.SUMMER.getSeasonName());
        System.out.println(Season.SUMMER.getSeasonDesc());
        
    }
}

// jdk5.0前定义枚举类方式
class Season {
    //1.声明当前类的对象的实例变量,使用private final 修饰
    private final String seasonName;
    private final String seasonDesc;
    
    //2.私有化类的构造器
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    
    //3.提供实例变量的get方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //4.创建当前类的实例对象,需要用public static final修饰
    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "白雪皑皑");
    
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

JDK 5.0 可使用 enum 定义枚举类

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

枚举类实现单例模式

package oopPlus.atguigu07._enum;

// 使用enum定义枚举类
public enum Season {
    // 利用枚举值实现单例模式
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "白雪皑皑");

    // 私有化实例变量
    private final String seasonName;
    private final String seasonDesc;

    // 私有化构造方法
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    // 提供实例变量的get方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

// 测试类
class SeasonTest {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);
        System.out.println(Season.SUMMER.getSeasonName());
        System.out.println(Season.SUMMER.getSeasonDesc());
    }
}

9、注解 Annotation

在这里插入图片描述

注解的应用场景

  • 生成文档相关的注解
  • 在编译时进行格式检查(三个基本注解)
  • 跟踪代码依赖性,实现替代配置文件功能

三个基本常用注解

  • @Override:限定重写父类方法,该注解只能用于方法
  • @Deprecated:用于表示所修饰的元素已过时。通常是因为所修饰的结构危险或有更好选择
  • @SuppressWarnings:抑制编译器警告

**元注解:**对现有的注解进行解释说明的注解。例如4个元注解

  • @Target:用于描述注解的适用范围。可以通过枚举类型ElementType的10个常量对象来指定TYPE,METHOD,CONSTRUCTOR,PACKAGE …
  • @Retention:用于描述注解的生命周期。可通过枚举类型RetentionPolicy的3个常量对象来指定 SOURCE(源代码),CLASS(字节码),RUNTIME(运行时)
  • @Documented:表面这个注解应该被 javadoc 工具记录
  • @Inherited:允许子类继承父类中的注解

10、包装类 Wrapper

包装类(Wrapper Class)是一种将基本数据类型包装成对象的类,Java 提供了一组包装类来实现这种功能。这些包装类位于 java.lang 包中,分别对应于基本数据类型 booleanbyteshortintlongfloatdoublechar
在这里插入图片描述

装箱和拆箱: 装箱是将基本数据类型转换为对应的包装类对象,而拆箱是将包装类对象转换为基本数据类型。例如:

// 装箱
Integer intValue = Integer.valueOf(10);

// 拆箱
int intValueUnboxed = intValue.intValue();

自动装箱和拆箱: 从 Java 5 开始,引入了自动装箱和拆箱的特性,允许直接将基本数据类型赋值给对应的包装类对象,或者将包装类对象赋值给对应的基本数据类型,而无需显式调用装箱和拆箱方法。

// 自动装箱
Integer intValue = 10;

// 自动拆箱
int intValueUnboxed = intValue;

在这里插入图片描述

String 与基本数据类型之间的转换

String 转换为基本数据类型:

  • 通过包装类的 parseXxx 方法: 每个基本数据类型的包装类都提供了 parseXxx 方法,用于将字符串转换为对应的基本数据类型。

    String str = "123";
    
    int intValue = Integer.parseInt(str);
    double doubleValue = Double.parseDouble(str);
    
  • 通过包装类的 valueOf 方法: 类似于 parseXxx 方法,valueOf 方法也可以用于将字符串转换为对应的包装类对象。

    String str = "123";
    
    Integer intValue = Integer.valueOf(str);
    Double doubleValue = Double.valueOf(str);
    

基本数据类型转换为 String:

  • 通过 String 类的 valueOf 方法: String 类的 valueOf 方法可以将基本数据类型转换为字符串。

    int intValue = 123;
    double doubleValue = 45.67;
    
    String strInt = String.valueOf(intValue);
    String strDouble = String.valueOf(doubleValue);
    
  • 通过 String.format 方法: 使用 String.format 方法进行格式化,也可以将基本数据类型转换为字符串。

    int intValue = 123;
    double doubleValue = 45.67;
    
    String strInt = String.format("%d", intValue);
    String strDouble = String.format("%.2f", doubleValue);
    

11、IDEA 常用快捷键

(1)通用型

在这里插入图片描述

(2)提高编写速度

在这里插入图片描述

在这里插入图片描述

(3)类结构、查找和查看源码

在这里插入图片描述

(4)查找、替换与关闭

在这里插入图片描述

(5)调整格式

在这里插入图片描述

二、异常处理

1、异常概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、Java 异常体系

1)Throwable

java.lang.Throwable 类是Java程序执行过程中发生的异常事件对应的类的根父类。

常用方法:

  • public void printStackTrace():打印异常的详细信息。
  • public String getMessage():获取发生异常的原因。

2)Error 和 Exception

在这里插入图片描述

3、编译时异常与运行时异常

在这里插入图片描述

常见的编译时异常

  • ClassNotFoundException
  • FileNotFoundException
  • IOException

常见的运行时异常

  • ArrayIndexOutOfBoundsException
  • NullPointerException
  • ClassCastException
  • NumberFormatException
  • InputMismatchException
  • ArithmeticException

4、异常处理方式

​ 使用 try-catch-finally 块可以捕获可能抛出的异常,并在 catch 块中处理异常。这种方式适用于需要针对不同类型的异常采取不同处理逻辑的情况。使用 finally 块可以确保资源在代码执行完毕后被正确释放,不管是否发生异常。这种方式适用于需要进行资源清理的情况。

try {
    // 可能抛出异常的代码块
    // ...
} catch (ExceptionType1 e1) {
    // 处理 ExceptionType1 异常的代码
} catch (ExceptionType2 e2) {
    // 处理 ExceptionType2 异常的代码
} finally {
    // finally 块,无论是否发生异常都会执行
    // 通常用于资源释放等操作
}

​ 使用 throws 关键字声明方法可能抛出的异常,由调用该方法的代码负责处理异常。这种方式适用于将异常传递给调用方处理的情况。

public void myMethod() throws MyException {
    // 可能抛出 MyException 异常的方法体
    // ...
}

​ 使用 throw 关键字手动抛出异常,可以根据特定条件抛出异常。这种方式适用于需要自定义异常信息或在特定条件下抛出异常的情况。

public void myMethod(int value) {
    if (value < 0) {
        throw new IllegalArgumentException("Value cannot be negative");
    }
    // 其他代码
}

5、自定义异常类

在 Java 中,你可以通过创建自定义异常类来表示特定的错误或异常情况,以便在程序中抛出和捕获这些异常。自定义异常类通常是继承自 Exception 类或其子类,或者继承自 RuntimeException 类或其子类。

  1. 创建自定义异常类:

    public class MyException extends Exception {
        // 无参构造方法
        public MyException() {
            super();
        }
    
        // 带有详细信息的构造方法
        public MyException(String message) {
            super(message);
        }
    
        // 带有详细信息和原因的构造方法
        public MyException(String message, Throwable cause) {
            super(message, cause);
        }
    
        // 带有原因的构造方法
        public MyException(Throwable cause) {
            super(cause);
        }
    }
    
  2. 在需要抛出异常的地方使用自定义异常类:

    public void myMethod() throws MyException {
        // 检查条件,如果不满足则抛出自定义异常
        if (conditionNotMet) {
            throw new MyException("Custom error message");
        }
        // 其他代码
    }
    
  3. 在调用处捕获并处理自定义异常:

    try {
        myMethod();
    } catch (MyException e) {
        // 处理自定义异常
        System.out.println("An error occurred: " + e.getMessage());
    }
    

三、多线程

1、程序、进程与线程

在这里插入图片描述

2、创建和启动线程

在这里插入图片描述

public class EvenNumberTest {
    public static void main(String[] args) {
        PrintNumber p1 = new PrintNumber();
        p1.start();

        PrintNumber p2 = new PrintNumber();
        p2.start();
        
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + i);
        }


    }
}

class PrintNumber extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

方式2:实现 Runnable 接口

① 创建一个实现Runnable接口的类

② 实现接口中的run() – 将此线程要执行的操作,声明在此方法体中

③ 创建当前实现类的对象

④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例

⑤ Thread类的实例调用start() : 1.启动线程 2.调用当前线程的run()

public class EvenNumberTest {
    public static void main(String[] args) {
        EvenNumberPrint p1 = new EvenNumberPrint();
        Thread t1 = new Thread(p1);
        t1.start();

        //匿名实现类的匿名对象
        new Thread(new Runnable() {
            //奇数
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    if (i % 2 != 0) System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }
        }).start();
    }
}

class EvenNumberPrint implements Runnable {

    @Override
    public void run() {
        //偶数
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

3、Thread类的常用方法和生命周期

1)构造方法

  • public Thread():分配一个新的线程对象。
  • public Thread(String name):分配一个指定名字的新的线程对象。
  • public Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口的run方法。
  • public Thread(Runnable target, String name):分配一个带有指定目标新的线程对象并指定名字。

2)常用方法

  • start():① 启动线程 ②调用线程的run()
  • run():将线程要执行的操作,声明在run()中
  • currentThread():获取当前执行代码对应的线程
  • getName():获取线程名
  • setName():设置线程名
  • sleep(long millis):静态方法,调用时,可以使得当前线程睡眠指定的毫秒数
  • yield():静态方法,一旦执行该方法,就释放CUP的执行权
  • join():在线程A中通过线程B调用 join() ,意味着A进入阻塞态,知道B执行结束,A才结束阻塞态,继续执行。

3)线程的优先级

  • getPriority():获取线程的优先级

  • setPriority():设置线程的优先级 ([1,10])

Thread类的内部声明的三个常量:

① MAX_PRIORITY(10):最高优先级

② MIN_PRIORITY(1):最低优先级

③ NORM_PRIORITY(5):普通优先级,默认main线程是普通优先级

4)生命周期

在这里插入图片描述

4、线程安全

在这里插入图片描述

Runnable 方式

public class WindowTest {
    public static void main(String[] args) {
        Window w = new Window();
        
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);
        
        t1.start();
        t2.start();
        t3.start();
    }
}

class Window implements Runnable {

    int ticket = 100;
    
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            synchronized (this) {

                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

Thread 方式

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window(); 
        
        t1.start();
        t2.start();
        t3.start();
    }
}

class Window extends Thread {
    
    static int ticket = 100;
    
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (Window.class) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

在这里插入图片描述

Runnable 方式

public class WindowTest {
    public static void main(String[] args) {
        Window w = new Window();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.start();
        t2.start();
        t3.start();
    }
}

class Window implements Runnable {

    int ticket = 100;
    boolean isFlag = true;
    
    @Override
    public void run() {
        while (isFlag) {
            show();
        }
    }

    public synchronized void show() {   //同步监视器是:this。即为 w ,是唯一的
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + ticket);
            ticket--;
        } else {
            isFlag = false;
        }
    }
}

Thread 方式

public class WindowTest {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        
        w1.start();
        w2.start();
        w3.start();
    }
}

class Window extends Thread {

    static int ticket = 100;
    static boolean isFlag = true;

    @Override
    public void run() {
        while (isFlag) {
            show();
        }
    }

    //public synchronized void show() { 同步监视器默认是:this。即为 w1,w2,w3 不唯一,线程不安全
    
    public static synchronized void show() {   //此时同步监视器为当前类本身, 即为 Window.class,是唯一的
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + ticket);
            ticket--;
        } else {
            isFlag = false;
        }
    }
}

5、死锁

在这里插入图片描述

6、Lock 锁

在这里插入图片描述

package Thread.at02.threadSafe._Lock;

import java.util.concurrent.locks.ReentrantLock;

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.start();
        t2.start();
        t3.start();
    }
}

class Window extends Thread {

    static int ticket = 100;

    //1.创建Lock的实例,需要确保多个线程共用一个Lock实例,故考虑将此对象声明为 static final
    private static final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                //2.执行lock()方法,锁定对共享资源的调用
                lock.lock();
                
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                } else {
                    break;
                }
            } finally {
                //3.unlock()的调用,释放对共享数据的锁定
                lock.unlock();
            }
        }
    }
}

在这里插入图片描述

7、线程的通信

在这里插入图片描述

在这里插入图片描述

面试题

在这里插入图片描述

生产者与消费者案例

public class ProducerConsumerTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);
        
        producer.setName("生产者");
        consumer.setName("消费者");
        
        producer.start();
        consumer.start();
    }
}

class Clerk {
    private int produceNum = 0;

    public synchronized void addProduce() {
        if (produceNum >= 20) {
            try {
                wait();     // 缓冲区满,阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            produceNum++;
            System.out.println(Thread.currentThread().getName() + "生产了第" + produceNum + "个产品");

            notify();   // 唤醒消费者线程
        }
    }

    public synchronized void minusProduce() {
        
        if (produceNum <= 0) {
            try {
                wait();     // 缓冲区空,阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + "消费了第" + produceNum + "个产品");
            produceNum--;
            
            notify();   // 唤醒生产者线程
        }
    }
}

//生产者
class Producer extends Thread {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("生产者开始生产");
        
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //生产产品
            clerk.addProduce();
        }
    }
}

//消费者
class Consumer extends Thread {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("消费者开始消费");
        
        while (true) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //消费产品
            clerk.minusProduce();
        }
    }
}

四、常用类与基础API

1、String 类

1)String 的理解与不可变性

在这里插入图片描述

在这里插入图片描述

2)String 的实例化与连接操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3)构造器与常用方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、StringBuffer 和 StringBuilder

StringBuffer: 可变的字符序列,是线程安全的,所有的方法都被 synchronized 关键字修饰,因此适合在多线程环境中使用。

StringBuilder: 可变的字符序列,不是线程安全的,因此在单线程环境中使用它更高效,性能更好。

扩容机制:

  1. 初始化容量: 当创建一个新的 StringBufferStringBuilder 对象时,会初始化一个初始容量。默认情况下,初始容量为 16 个字符。
  2. 扩容条件: 当添加的字符数超过了当前容量时,就需要进行扩容。扩容的条件通常是当前容量不足以容纳要添加的字符。
  3. 增长策略: 在扩容时,StringBufferStringBuilder 会根据其增长策略来确定新容量的大小。默认扩容为原有容量的 2 倍 + 2 。
  4. 复制内容: 扩容时,会创建一个新的字符数组,并将原来的字符数组中的内容复制到新数组中。这样做是为了保留之前添加的字符,并确保在新的容量下仍然可以继续添加字符。

在这里插入图片描述

如果大体确定要操作的字符个数,建议使用带 int capacity 参数的构造器,可以避免底层多次扩容。

常用API

在这里插入图片描述

在这里插入图片描述

3、日期时间API

1)JDK8 之前的API

Date类常用方法:

  1. getTime()
    • 返回自1970年1月1日00:00:00以来的毫秒数。
  2. setTime(long time)
    • 设置Date对象的时间,参数为自1970年1月1日00:00:00以来的毫秒数。
  3. getYear()
    • 获取年份(从1900年开始)。
  4. getMonth()
    • 获取月份(从0开始,0表示一月,11表示十二月)。
  5. getDate()
    • 获取日期(月份中的第几天)。
  6. getHours()getMinutes()getSeconds()
    • 获取小时、分钟、秒。
  7. setYear(int year)setMonth(int month)setDate(int date)
    • 设置年、月、日。

Calendar类常用方法:

  1. getInstance()
    • 获取一个默认时区和语言环境的Calendar对象实例。
  2. set(int year, int month, int date)
    • 设置年、月、日。
  3. setTime(Date date)
    • 设置Calendar对象的时间,参数为Date对象。
  4. get(int field)
    • 获取指定字段的值,如Calendar.YEARCalendar.MONTHCalendar.DATE等。
  5. add(int field, int amount)
    • 对指定字段进行加减操作,如加一年、减一个月等。
  6. getTime()
    • Calendar对象转换为Date对象。
  7. getTimeInMillis()
    • 获取自1970年1月1日00:00:00以来的毫秒数。
  8. setTimeZone(TimeZone zone)
    • 设置时区。

2)JDK8 中的API

在这里插入图片描述

在这里插入图片描述

本地日期时间:LocalDate、LocalTime、LocalDateTime

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4、Comparable 和 Comparator

1)使用 Comparable 接口实现自然排序

在这里插入图片描述

@Override
public int compareTo(Object o) {
    if (o == this) {
        return 0;
    }
    
    if (o instanceof Product) {
        Product p = (Product) o;
        
        return Double.compare(this.price, p.price);
    }
    
    throw new RuntimeException("类型不匹配");
}

2)使用 Comparator 接口实现定制排序

在这里插入图片描述
在这里插入图片描述

@Test
public void test() {
    String[] arr = new String[]{"Tom", "Jerry", "Tony", "Rose", "Jack", "Lucy"};
    
    Arrays.sort(arr, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof String && o2 instanceof String) {
                String s1 = (String) o1;
                String s2 = (String) o2;
                
                return -s1.compareTo(s2);
            }
            
            throw new RuntimeException("类型不匹配");
        }
    })
    
}

在这里插入图片描述

五、集合框架

在这里插入图片描述

1、Collection 常用方法

在这里插入图片描述
在这里插入图片描述

2、迭代器 Iterator

在这里插入图片描述

在这里插入图片描述

3、List

在这里插入图片描述

在这里插入图片描述

4、Set

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5、Map

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

@Test
public void test() {
    HashMap hashMap = new HashMap();
    hashMap.put("AA", 56);
    hashMap.put(1, 22);
    hashMap.put("BB", 33);
    hashMap.put(new Person("Tom", 32), 23);

    System.out.println(hashMap);

    Set keySet = hashMap.keySet();
    System.out.println(keySet);

    Collection values = hashMap.values();
    System.out.println(values);

    Set entrySet = hashMap.entrySet();
    Iterator iterator = entrySet.iterator();
    while(iterator.hasNext()) {
        Map.Entry entry = (Map.Entry) iterator.next();
        System.out.println(entry.getKey() + "-->" + entry.getValue());
    }
}

在这里插入图片描述

@Test //定制排序 
public void test() {
    Comparator comparator = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 == o2) return 0;

            if (o1 instanceof Person && o2 instanceof Person) {
                Person p1 = (Person) o1;
                Person p2 = (Person) o2;
                int flag = p1.getName().compareTo(p2.getName());
                if (flag != 0) return flag;

                return p1.getAge() - p2.getAge();
            }

            throw new RuntimeException("类型不匹配");
        }
    };

    TreeMap treeMap = new TreeMap(comparator);

    Person p1 = new Person("Tom", 1);
    Person p2 = new Person("Yan", 21);
    Person p3 = new Person("Dom", 22);
    Person p4 = new Person("Jerry", 18);
    Person p5 = new Person("LiHua", 25);

    treeMap.put(p1, 13);
    treeMap.put(p2, 36);
    treeMap.put(p3, 77);
    treeMap.put(p4, 13);
    treeMap.put(p5, 5);

    Set entrySet = treeMap.entrySet();
    for (Object entry: entrySet) {
        System.out.println(entry);
    }
}

在这里插入图片描述

6、Collections 工具类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

六、泛型

1、泛型的使用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package Generic.use;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Predicate;

public class Exer {
    public static void main(String[] args) {
        // 1、创建ArrayList集合对象,指定泛型为<Integer>
        ArrayList<Integer> arrayList = new ArrayList<>();

        // 2、添加5个[0, 100)的随机整数
        for (int i = 0; i < 5; i++) {
            int random = (int) (Math.random() * 100);
            arrayList.add(random);
        }

        // 3、foreach遍历
        for (int i: arrayList) {
            System.out.println(i);
        }
        
        // 4、用集合的removeIf删除偶数,为Predicate接口指定泛型<Integer>
        arrayList.removeIf(new Predicate<Integer>() {
            @Override
            public boolean test(Integer value) {
                if (value % 2 == 0) return true;
                
                return false;
            }
        });
        
        System.out.println("--------");
        
        // 5、使用Iterator迭代器输出剩下元素,为Iterator接口指定泛型<Integer>
        Iterator<Integer> iterator = arrayList.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

2、自定义泛型

1)自定义泛型类

​ 自定义泛型是在编写Java代码时定义参数化类型的一种方式,它允许你编写更加灵活、可重用的代码,同时提高了代码的类型安全性。通过使用泛型,你可以编写一次代码,以不同类型的参数进行多次使用。

public class MyGenericClass<T> {
    private T data;

    public MyGenericClass(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public void printData() {
        System.out.println("Data: " + data);
    }
}

​ 在上面的示例中,MyGenericClass是一个泛型类,它有一个类型参数T。在实例化这个类时,你可以指定具体的类型作为参数,比如IntegerString等。例如:

MyGenericClass<Integer> intObj = new MyGenericClass<>(10);
System.out.println(intObj.getData()); // 输出:10

MyGenericClass<String> stringObj = new MyGenericClass<>("Hello");
System.out.println(stringObj.getData()); // 输出:Hello

在这里插入图片描述

在这里插入图片描述

2)自定义泛型方法

​ 除了泛型类之外,Java还支持泛型方法。你可以在普通类、接口、抽象类中定义泛型方法,允许这些方法在被调用时接受不同类型的参数。

public class MyUtils {
    public static <T> T findMax(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }

        T max = array[0];
        for (T item : array) {
            if (item.compareTo(max) > 0) { // 这里假设T是Comparable类型
                max = item;
            }
        }

        return max;
    }
}

在这个示例中,findMax方法是一个泛型方法,它可以接受任意类型的数组作为参数,并且返回该数组中的最大值。你可以将不同类型的数组传递给这个方法,并得到相应类型的最大值。

Integer[] intArray = {3, 6, 2, 8, 1};
System.out.println(MyUtils.findMax(intArray)); // 输出:8

String[] stringArray = {"apple", "banana", "orange"};
System.out.println(MyUtils.findMax(stringArray)); // 输出:orange

3、通配符 ‘?’

在这里插入图片描述

在这里插入图片描述

七、数据结构和集合源码

1、数据结构

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、ArrayList 源码

在这里插入图片描述

在这里插入图片描述

扩容机制

在这里插入图片描述

3、Vector 源码

在这里插入图片描述

扩容机制

在这里插入图片描述

4、LinkedList 源码

在这里插入图片描述

底层使用双向链表

在这里插入图片描述

添加一个元素

在这里插入图片描述

5、HashMap

1)JDK7

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2)JDK8

在这里插入图片描述

在这里插入图片描述

对于:

HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put(“AA”, 11);

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // 所有其他字段默认
}

其中:

static final float DEFAULT_LOAD_FACTOR = 0.75f;
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//哈希值1 —> 哈希值2
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);

    // ...略...

    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

扩容

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;

    // ...略...
    
    return newTab;
}

Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    // ...略...
}

在这里插入图片描述

6、LinkedHashMap

在这里插入图片描述

7、HashSet和LinkedHashSet

HashSet(底层是HashMap)

在这里插入图片描述

LinkedHashSet(底层是LinkedHashMap)

在这里插入图片描述

在这里插入图片描述

八、File类与IO流

1、File 类

1)概述

在这里插入图片描述

2)构造方法

在这里插入图片描述

在IDEA中,如果使用单元测试方法,相对于当前的 module 来讲;

​ 如果使用 main() 方法,相对于当前的 project 来讲

3)常用方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、IO 流原理及分类

1)IO 流原理

在这里插入图片描述

在这里插入图片描述

2)流的分类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3)API

在这里插入图片描述

3、FileReader 和 FileWriter 读写数据

在这里插入图片描述

1)FileReader 读数据

package File.file;

import org.junit.Test;

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

//读取hello.txt的内容
public class FileReaderTest {
    @Test
    public void test1() throws IOException {
        //1.创建File类的对象,对于文件
        File file = new File("hello.txt");

        //2.创建输入型字符流,用于读数据
        FileReader fr = new FileReader(file);

        //3.读取数据
        int data = fr.read();
        while (data != -1) {
            System.out.print((char) data);
            data = fr.read();
        }

        //4.流资源的关闭
        fr.close();
    }

    //try-catch-finally 确保资源关闭
    @Test
    public void test2() {
        FileReader fr = null;
        try {
            //1.创建File类的对象,对于文件
            File file = new File("hello.txt");

            //2.创建输入型字符流,用于读数据
            fr = new FileReader(file);

            //3.读取数据
            int data = fr.read();
            while (data != -1) {
                System.out.print((char) data);
                data = fr.read();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流资源的关闭
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    //优化,每次读取多个字符放入数组中,减少与磁盘的交互
    @Test
    public void test3() {
        FileReader fr = null;
        try {
            //1.创建File类的对象,对于文件
            File file = new File("hello.txt");

            //2.创建输入型字符流,用于读数据
            fr = new FileReader(file);

            //3.读取数据
            char[] cbuffer = new char[5];
            int len = fr.read(cbuffer);
            while (len != -1) {
                for(char c: cbuffer) {
                    System.out.print(c);
                }
                
                len = fr.read(cbuffer);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流资源的关闭
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2)FileWriter 写数据

package File.file;

import org.junit.Test;

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

//将内存中数据写出到指定文件中
public class FileWriterTest {
    @Test
    public void test1() {
        FileWriter fw = null;   
        try {
            //1.创建File类对象,指明要写出的文件名称
            File file = new File("info.txt");

            //2.创建输出流
            fw = new FileWriter(file);  //覆盖的构造器

            //3.写出的具体过程
            fw.write("hahaha\n");
            fw.write("咯咯123\n");

            System.out.println("输出成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3)读写数据

package File.file;

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

//复制一份hello.txt文件,命名为hello_copy.txt
public class FileReaderWriterTest {
    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象
            File srcFile = new File("hello.txt");
            File destFile = new File("hello_copy.txt");

            //2.创建输入流、输出流
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);

            //3.数据的读入和写出
            char[] cbuffer = new char[5];
            int len;
            while ((len = fr.read(cbuffer)) != -1) {
                fw.write(cbuffer, 0, len);
            }

            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流资源
            try {
                if (fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

4、FileInputStream 和 FileOutputStream

在这里插入图片描述

在这里插入图片描述

package File.file;

import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

//复制一份image.jpg,命名为image_copy.jpg
public class FileStreamTest {
    @Test
    public void test1() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1.创建相关的File类的对象
            File file1 = new File("image.jpg");
            File file2 = new File("image_copy.txt");

            //2.创建相关的字节流
            fis = new FileInputStream(file1);
            fos = new FileOutputStream(file2);

            //3.数据的读入和写出
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            System.out.println("复制成功");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、处理流1—缓冲流

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1)BufferedInputStream 和 BufferedOutputStream

package File.buffer;

import org.junit.Test;

import java.io.*;

public class BufferedStream {
    @Test
    public void test1() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1.创建相关的File类的对象
            File file1 = new File("image.jpg");
            File file2 = new File("image_copy.jpg");

            //2.创建相关的字节流、缓冲流
            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);

            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            
            //3.数据的读入和写出
            byte[] buffer = new byte[50];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            System.out.println("复制成功");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (bis != null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2)BufferedReader 和 BufferedWriter

package File.buffer;

import org.junit.Test;

import java.io.*;

public class BufferedReaderWriterTest {
    @Test
    public void test1() {
        BufferedReader br = null;
        try {
            File file = new File("hello.txt");

            br = new BufferedReader(new FileReader(file));

            char[] cBuffer = new char[1024];
            int len;
            while ((len = br.read(cBuffer)) != -1) {
                String str = new String(cBuffer, 0, len);
                System.out.println(str);
            }
            
            /*  (readLine(): 每次读取一行文本的数据,返回的字符不包括换行符)
            *   
            *   String data;
            *   while((data = br.readLine()) != null) {
            *       System.out.println(data + "\n");
            *   }
            */

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void test2() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //1.创建相关的File类的对象
            File file1 = new File("hello.txt");
            File file2 = new File("hello_copy.txt");

            //2.创建相关的字符流、缓冲流
            br = new BufferedReader(new FileReader(file1));
            bw = new BufferedWriter(new FileWriter(file2));

            //3.数据的读入和写出
            String data;
            while((data = br.readLine()) != null) {
                bw.write(data);
                bw.newLine();   //表示换行操作
                bw.flush();     //刷新的方法,调用时主动将内存中的数据写到磁盘文件中
            }
            System.out.println("复制成功");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (br != null)
                    br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bw != null)
                    bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

6、处理流2—转换流

  • InputStreamReader

  • OutputStreamWriter

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package File.IOStream;

import java.io.*;

public class IOStreamRW {
    public static void main(String[] args) {
        // 定义文件对象
        File file1 = new File("input.txt");
        File file2 = new File("output.txt");

        // 定义输入流和输出流变量,用于后续的资源管理
        InputStreamReader isr = null; // 声明字符输入流变量
        OutputStreamWriter osw = null; // 声明字符输出流变量

        try {
            // 创建文件输入流,将字节流转换为字符流,并指定字符集为UTF-8
            FileInputStream fis = new FileInputStream(file1);
            isr = new InputStreamReader(fis, "UTF-8");

            // 创建文件输出流,指明内存中字符存储到文件中的字节过程使用的编码集为GBK
            FileOutputStream fos = new FileOutputStream(file2); 
            osw = new OutputStreamWriter(fos, "GBK"); 

            // 读写过程
            char[] cBuffer = new char[1024]; // 创建一个字符数组,用于暂存读取到的数据
            int len; // 用于记录每次读取到的字符数
            while((len = isr.read(cBuffer)) != -1) { // 循环读取数据,直到读取到文件末尾
                osw.write(cBuffer, 0, len); // 将读取到的数据写入到输出流中
            }
            System.out.println("操作完成");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (osw != null)
                    osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (isr != null)
                    isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

7、处理流3—对象流

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package File.IOStream;

import org.junit.Test;

import java.io.*;

public class ObjectInputOutputStreamTest {
    
    //序列化过程:使用ObjectOutputStream流实现。将内存中的Java对象保存在文件中或通过网络传输出去
    @Test
    public void test1() throws IOException {
        
        File file = new File("object.txt");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        
        //写出数据即为序列化过程
        oos.writeUTF("江山如此多娇,引无数英雄竞折腰");
        oos.flush();
        
        oos.writeObject("仰天长笑出门去,我辈岂是蓬蒿人");
        
        oos.close();
    }
    
    //反序列化过程:使用ObjectInputStream流实现。将文件中的数据或网络传输过来的数据还原为内存中的Java对象
    @Test
    public void test2() throws IOException, ClassNotFoundException {
        
        File file = new File("object.txt");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        
        //读取文件中的对象 (反序列化的过程)
        String str1 = ois.readUTF();
        System.out.println(str1);
        
        String str2 = (String) ois.readObject();
        System.out.println(str2);
        
        ois.close();
    }
}

九、网络编程

1、网络传输三要素

  • IP 地址
  • 端口号
  • 网络通信协议

在这里插入图片描述

2、InetAddress

InetAddress 类的一个实例就代表一个具体的ip地址。

实例化方法:

  • InetAddress getByName(String host):获取指定 ip 对应的 InetAddress 的实例。
  • InetAddress getLocalHost():获取本地 ip 对应的 InetAddress 的实例。

常用方法:

  • getHostName():获取对应的域名
  • getHostAddress():获取对应 ip 地址

3、TCP 与 UDP 协议

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1)Socket

在这里插入图片描述

2)TCP 网络编程

在这里插入图片描述

package Socket.TCP;

import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

//客户端发送内容给服务端,服务端进行打印
public class TCPTest1 {
    //客户端
    @Test
    public void client() {
        Socket socket = null;
        OutputStream os = null;
        try {
            //1.创建一个Socket
            InetAddress inetAddress = InetAddress.getByName("192.168.241.1");
            int port = 8989;    //对方的端口号
            socket = new Socket(inetAddress, port);//指明对方的IP地址和端口号

            //2.发送数据
            os = socket.getOutputStream();
            os.write("你好,我是客户端".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭socket、流
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //服务器端
    @Test
    public void server() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            //1.创建一个ServerSocket
            int port = 8989;    //自己的端口号
            serverSocket = new ServerSocket(port);

            //2.调用accept(),接收客户端的Socket
            System.out.println("服务器端已开启......");
            socket = serverSocket.accept();

            System.out.println("收到了来自于" + socket.getInetAddress().getHostAddress() + "的连接");

            //3.接收数据
            is = socket.getInputStream();
            byte[] buffer = new byte[5];
            int len;
            baos = new ByteArrayOutputStream();
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            System.out.println(baos);

            System.out.println("数据接收完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (baos != null) {
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (serverSocket != null) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        System.out.println("客户端已关闭......");

    }
}

3)UDP 网络编程

package Socket.UDP;

import org.junit.Test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPTest {
    //发送端
    @Test
    public void sender() throws IOException {
        //1.创建DatagramSocket实例
        DatagramSocket ds = new DatagramSocket();
        
        //2.将数据,目的ip,目的端口号封装在DatagramPacket数据报中
        InetAddress inetAddress = InetAddress.getByName("192.168.241.1");
        int port = 9090;
        byte[] bytes = "我是发送端".getBytes("utf-8");
        DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, inetAddress, port);
        
        //3.发送数据
        ds.send(packet);
        
        ds.close();
    }

    //接收端
    @Test
    public void receiver() throws IOException {
        //1.创建DatagramSocket实例
        int port = 9090;
        DatagramSocket ds = new DatagramSocket(port);

        //2.创建数据报的对象,用于接收发送端发送的数据
        byte[] buffer = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        
        //3.接收数据
        ds.receive(packet);
        
        //4.获取数据,打印到控制台
        String str = new String(packet.getData(), 0, packet.getLength());
        System.out.println(str);
        
        ds.close();
    }
}

十、反射机制

1、反射的概念

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、反射和Class的理解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package reflection;

import org.junit.Test;

//获取Class实例的几种方法
public class ClassTest {
    @Test
    public void test1() throws ClassNotFoundException {
        //1.调用运行时类的静态属性: class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        
        //2.调用运行时类的对象的getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
        
        //3.调用Class的静态方法forName(String className)
        String className = "reflection.Person";  //全类名
        Class clazz3 = Class.forName(className);
        System.out.println(clazz3);
    }
}

在这里插入图片描述

3、类的加载过程与类加载器

1)类的加载过程

在这里插入图片描述

在这里插入图片描述

2)类加载器 (classloader)

在这里插入图片描述

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值