【高级语法篇】Java必备基础(思维导图+代码)


在这里插入图片描述

⭐代码和思维导图详见仓库:Sivan_Xin的代码仓库—Java语法。如果觉得有帮助的话,可以帮作者点一个star⭐哦~

🎉 关于Java语法部分的知识体系主要分三篇文章进行讲解:
🎉【基础语法篇】【面向对象篇】【高级语法篇】
🎉大家可以在日常的学习生活中进行参考,笔记内容并不是绝对权威,所以请保持自己独立思考的能力。

本文共16000字,读完约需30分钟。

  • 思维导图以其中一张为例:在这里插入图片描述

多线程

基本概念

  • 程序、进程、线程
    程序:静态的代码块。
    进程:进程作为资源分配的单位,也就是运行起来的程序,存在生命周期。
    线程:进程进一步细化为线程,是程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程。每个进程有不同的内存区域,而线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器。

多个线程可以对内存进行操作,这时就可能会出现一些问题。一个Java程序至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。

并行与并发:
并行:多个CPU同时执行多个任务。
并发:一个CPU同时执行多个任务。比如:秒杀、多个人做同一件事。

问:何时需要多线程?

  • 程序需要同时执行两个或多个任务。
  • 程序需要实现一些需要等待的任务,如用户输入、文件读写等。
  • 需要后台运行的程序。

线程的创建和使用

创建多线程方式一:使用Thread类。
1. 创建一个继承Thread类的子类。
2. 重写Thread类的run方法 -->将线程执行的操作声明在run中。
3. 创建Thread类的子类的对象。
4. 通过对象调用start方法。
start方法的作用:1. 启动线程 2. 调用run方法。

创建多线程方式二:实现Runnable接口。
1. 实现Runnable接口
2. 重写run()方法
3. 创建实现接口的类的对象
4. 使用Thread,将对象传入构造器中,new新的线程对象
5. 使用线程对象调用start()方法

  • 两者比较
    开发中:优先选择实现Runnable接口的方式。
    原因:
    1. 没有类的单继承性的局限性
    2. Runnable更适合来处理多个线程有共享数据的情况。

相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

  • Thread类中的常用方法
    1. start():启动当前线程;调用当前线程的run()
    2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    3. currentThread():静态方法,返回执行当前代码的线程
    4. getName():获取当前线程的名字
    5. setName():设置当前线程的名字
    6. yield():释放当前cpu的执行权
    7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
    8. stop():已过时。当执行此方法时,强制结束当前线程。
    9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
    10. isAlive():判断当前线程是否存活
  • 线程的调度

时间片策略; 抢占式:高优先级抢占低优先级的线程。

  • 线程的优先级
  1. 线程的三大优先级
    MAX_PRIORITY:10

    MIN _PRIORITY:1

    NORM_PRIORITY:5 -->默认优先级
    高优先级的线程要抢占低优先级线程cpu的执行权。

  2. 如何获取和设置当前线程的优先级:
    getPriority():获取线程的优先级
    setPriority(int p):设置线程的优先级

说明: 只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

线程的通信

线程与线程之间可以进行通信交互。

wait()方法:使当前线程阻塞,并将锁释放。与sleep不同,sleep只是将线程休眠,并没有释放锁。
notify()方法:释放正在wait()的线程。
notifyAll()方法:释放所有正在wait()的线程,按照优先级释放。

  • 注意
  1. 这三个方法只能出现在同步代码块或同步方法中。
  2. 调用者必须是同步代码块或同步方法中的同步监视器。
  3. 三者是定义在java.lang.Object类中的。

线程的生命周期

两张图片:
在这里插入图片描述

在这里插入图片描述

Thread.State枚举类中定义了线程的几种状态:

  • 新建 :当一个Thread类或子类对象被声明创建时。
  • 就绪:处于新建状态的线程被start()后,进入线程队列等待CPU时间片,此时已经具备运行条件。
  • 运行:线程被调度,run()方法定义了线程的操作和功能。
  • 阻塞:线程被人为挂起或执行输入输出操作时。
  • 死亡:线程完成工作或线程被提前强制性中值。

线程的同步

  • 为什么使用线程的同步?
    多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
    Java中会通过同步机制,解决线程安全问题。

synchronized

  • 方式一:同步代码块
 synchronized(同步监视器){
 //说明:1. 操作共享数据的代码,即为需要被同步的代码。
 //       2. 共享数据:多个线程共同操作的变量。
 //       3. 同步监视器(锁):任意一个类的对象,但是多个线程必须共用同一把锁。
 //     在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
 }
  • 方式二:同步方法
  1. 如果操作共享数据的代码完整的声明在一个方法中,不妨将此方法声明为同步的。
  2. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
  3. 非静态的同步方法,同步监视器是:this [使用Runnable]
    静态的同步方法,同步监视器是:当前类本身 [使用Thread]
  • 同步的好处与局限性
    同步解决了线程的安全问题,但是在操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低。

  • 线程的死锁问题
    不同的线程分别占据对方需要的资源(同步监视器对象)不放弃,就形成了线程的死锁。出现死锁后,不会出现异常,不会出现提示,只是线程都处于阻塞状态,无法继续。

Lock(锁)

从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。ReentrantLock类实现了Lock接口,在实现线程安全的控制中,比较常用的是ReentrantLock。

JDK5.0新增线程创建方式

  • 新增方式一:实现Callable接口
  1. 相比run()方法,可以有返回值。
  2. 方法可以抛出异常。
  3. 支持泛型的返回值。
  4. 需要借助FutureTask类,比如获取返回结果等。

另外的,Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口。

  • 新增方式二:使用线程池
    背景:对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完返回池中。
    好处:
  1. 提高响应速度(减少创建新线程所需的时间)
  2. 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
  3. 便于线程管理
  • 线程池相关API:
    ExecutorService类、Executors工具类、ThreadPoolExecutor类。

推荐使用ThreadPoolExecutor类来创建线程池,避免使用ExecutorService类造成的OOM。

Java集合框架

概述

Java 集合框架主要包括两种类型的容器:

  • 单列集合框架:Collection接口,在该接口下还有两个子接口ListSet
    其中,List存储有序、可重复元素(动态数组)。Set存储无序、不可重复元素。
  • 双列集合框架:Map,存储(key—value)映射。

通用语句:ArrayList<E> objectName = new ArrayList<>;

Collection接口

Collection的使用

  • 常用API
    参考官方文档/代码即可。
  • 集合<——>数组
    集合—>数组:toArray方法。
    数组—>集合:Arrays.asList方法。官方文档中是这么介绍的:返回由指定数组支持的固定大小的列表。此方法与Collection.toArray()结合使用,作为基于数组和基于集合的API之间的桥梁。

补充知识:

  • ArrayList,HashSet,HashMap等都可以用sout进行输出。因为在类的方法中重写了toString方法。
  • 可变形参
    语法:数据类型···变量名。
    Arrays.asList()方法中传入的是一个可变形参。表示传入的参数可以是一个或多个数/数组。

iterator(迭代器)

iterator可用于迭代实现了Collection接口的集合。

  • 迭代器执行原理
    我们这里可以把iterator对象看成一个指针。
    调用next() 1. 指针下移 2. 返回下移后的元素。
    调用hasNext() 往下看还有没有其他元素。

补充:for:each循环遍历集合就是内部调用迭代器来实现的。

子接口

List接口

List接口:存储有序的、可重复的数据——动态数组。
接口的具体实现类:ArrayListLinkedList、Vector。

  • 常用API
    参考官方文档/代码即可。

  • LinkedList的源码分析

  1. 使用无参构造器LinkedList list = new LinkedList()—> 内部声明了Node类型的first和last属性。
  2. 添加操作,创建了一个Node对象,存入数据。
  • Vector的源码分析
    Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
    在扩容方面,默认扩容为原来的数组长度的2倍。
Set接口

主要存储无序的、不可重复的数据,Set中没有新的方法,都是从Collection继承而来的。
向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()。

  • 理解无序和不可重复
  1. 无序性:存储在数据的底层数组中并非按照数组的索引来添加,而是根据数据的哈希值决定。
  2. 不可重复性:保证添加相同的元素时,使用equals()判断不能返回true。

Set的具体实现类:
HashSet、LinkedHashSet、TreeSet

  • 具体实现类

  • HashSet
    Set的主要实现类,线程不安全,可以存储null值。基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。

  • LinkedHashSet
    HashSet的子类。在添加数据的同时,每个数据还维护了pre和next两个引用。LinkedHashSet的底层是一个数组+双向链表。意义和好处是LinkedHashSet中的元素顺序可以保证,也就是说遍历序和插入序是一致的

  • TreeSet
    向TreeSet中添加数据,要求是相同类的对象。TreeSet的底层使用红黑树。

  • TreeSet的两种排序方式
    自然排序(需要实现Comparable接口,重写compareTo()方法) 和 定制排序(构造器传参Comparator)
    如果使用定制排序,就向TreeSet的构造函数中传入一个Comparator对象。
  1. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0。不再是equals()。
  2. 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals()。

Map

Map存储双列数据(key—value)(以HashMap为例)
key使用Set存储,无序,不能重复。——>存储自定义的类的对象时,要重写equals和hashCode。
value使用Collection存储可重复,无序的数据。——>存储自定义的类的对象时,要重写equals方法。
Entry用Set容器存储,无序,不可重复。调用put方法时放进去的就是Map.Entry对象。(key和value)

  • 具体实现类
  • HashMap:是Map的主要实现类,线程不安全,效率高。可以存储null的数据。
    • LinkedHashMap:在遍历map元素时,可以按照添加的顺序来遍历 。
  • TreeMap:保证按照添加的key—value进行排序,实现排序遍历。此时考虑key的自然排序和定制排序。底层使用红黑树。
  • Hashtable:作为古老的实现类,线程安全,效率低。不能存储null的key—value。
  • Properties:properties类继承于Hashtable类,实现了Map等接口。常用来处理配置文件。key和value都是String类型。配置文件后缀:properties。

Map常用API
可查阅文档/代码仓库。

Collections工具类

Collections类直接继承于Object接口,是集合类的一个工具类。这个类不能被实例化(私有构造器),所有的方法都是静态的(可通过类名调用)。包含有关集合操作的静态方法,可以实现对各种集合的搜索、排序、线程安全等。

泛型

概述

  • 泛型是什么?
    元素类型不确定,所以设计一个参数,这个参数叫做泛型。
    例如ArrayList< E>,< E>就是传入一个泛型的意思。
    总之,就是通过一个标识,影响方法的参数类型。
  • 使用泛型的作用?
    1.编译时会进行类型检查,保证数据的安全。
    2.避免了类型转换异常出现的情况。
  • 使用泛型的注意?
    1. 使用泛型,不可以用基本数据类型,必须用其包装类
    2. 泛型只是一个标签,不一定传入的都是基本数据类型的包装类,也可以是其他类。
    3. Java集合接口和集合类jdk1.5之后都被修改为带泛型的结构。
    4. 子类继承父类时,父类指明了泛型类型,子类实例化对象时不需要使用泛型。
    5. 子类继承父类时,父类没有指明泛型类型,子类实例化对象时需要使用泛型。
    6. 异常类不能是泛型的。
    7. 静态方法中不能使用泛型。
    8. 泛型的嵌套(HashMap构造迭代器):
    Map<String,Integer> map = new HashMap<>();

Iterator <Map.Entry<String,Integer>> iterator = map.entrySet().iterator();

while(iterator.hasNext()){
            Map.Entry<String, Integer> e = iterator.next();
            String key = e.getKey();
            Integer value = e.getValue();
            System.out.println(key + "----" + value);
        }      

自定义泛型类、泛型方法、泛型继承

  • 自定义泛型程序举例:
//定义带泛型的类
public class Student<T> {
    int age;
    String name;
    T describe;
    public Student(int age, String name, T describe) {
        this.age = age;
        this.name = name;
        this.describe = describe;
    }
}
//1. 继承父类时,子类指定泛型的类型,此时子类不是泛型。例:
public class Generic extends fatherGeneric<String>

//接口类似。如果一个类实现的接口给出了具体的泛型,该类不需要写出泛型,反之该类需要给出泛型。

class class -name <type-param-list> implements interface -name <type-arg-list> {

//2. 继承父类时,子类没有指定泛型的类型,此时子类是泛型。
public class Generic <T>extends Student<T>{

    //泛型类的构造器不用加<>
    public Generic(int age, String name, T describe) {
        super(age, name, describe);
    }
    
   
    //泛型方法:
    //泛型方法与所在类是否是泛型无关。
    //泛型方法可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
    public static <E> List toArray(E []e){
        List<E> list=new ArrayList<>();
        for(E e1 :e){
            list.add(e1);
        }
        return list;
    }

    //静态方法中,不能使用泛型。泛型是在实例化对象的时候(调用构造器)使用的,
    // 而静态方法在加载类的时候就优先加载了(静态优先,构造随后)。
    public static int Demo(T t){  //错误

    }

    public static void main(String[] args) {
        //实例化对象时,要使用泛型。
        Generic<String> generic=new Generic<>(12,"Wang","good");
        List<Integer> list=Generic.toArray(new Integer[]{1,2,3});
        System.out.println(list);
    }
}
  • 继承中的泛型
  1. 虽然类A是类B的父类,但是G< A> 和G< B >二者不具备子父类关系,二者是并列关系。
    例如:
    List<Object> list1 = null;
    List<String> list2 = new ArrayList<>();
    //编译不通过
    // list1 = list2;
  1. 类A是类B的父类,A 和B 二者是子类和父类的关系。
    例如:
    List<String> list1 = null;
    ArrayList<String> list2 = null;
    //编译通过
    list1 = list2;

泛型的通配符

  • 通配符
    类A是类B的父类,G< A>和G< B>是没有关系的,二者共同的父类是:G<?>
 //通配符的使用举例
       ArrayList<Object> list=new ArrayList<>();
       ArrayList<String> arrayList=new ArrayList<>();
       ArrayList<?> a=new ArrayList<>();
       //G<?>是G<A> G<B>的父类,故可以父类引用指向子类对象。
       a=list;
        //添加(写入):对于Arraylist<?>就不能向其内部添加数据。
        //查找(读取):对于Arraylist<?>可以读取数据,返回Object类型。
  • 有限制条件的通配符
    <? extends Person>
    表示?可以接受小于等于Person的类。(-无穷 ,Person],例如Student等…

    <? super Person>
    表示?可以接受大于等于Person的类。[Person,+无穷),例如Object等…

  • 添加操作和查找操作
    添加操作(以List.add为例)
    当执行添加操作时,只可以添加小于等于当前区间的子类。(添加Student、Person;不可以添加Object)
    查找操作(以List.get为例)
    当执行查找操作时,使用当前区间的最大类来接受值。(用Object类来接收)

IO流

File文件类

作用: Java中使用File类的对象来充当文件夹/文件目录。
注意: 在File类中,只是对文件进行创建、删除、重命名等操作,并未进行读取和修改文件内容(IO流)。

  • 文件引用路径:
  1. 相对路径:相对于当前包的位置,文件在当前包下创建。
  2. 绝对路径:包含盘符在内的文件或文件目录的路径。

相对路径写在main方法下,会对应着当前工程,进而创建文件在当前整体工程下。并非当前Moudle下。

IO流原理及流的分类

  • IO流原理
    IO是input/output的缩写,用于处理设备之间的数据传输。在Java程序中,对于数据的输入/输出操作是以“流”的形式进行的。

  • 流的分类
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-47doCRZL-1656395185361)(en-resource://database/1322:1)]

  1. 四个抽象基类(按操作的数据分):
  • 字节流:InputStream/OutputStream。(图片,101010)
  • 字符流:Reader/Writer。(文字,字节的基础上编码)
  1. 按流的角色分为:
  • 节点流(文件流):FileInputStream/FileOutputStream、FileReader/FileWriter。
  • 处理流:除了节点流的其他流。

节点流

在这里插入图片描述

节点流等同于流的基础,所有其他处理流都是建立在节点流的基础之上的。
FileInputStream/FileOutputStream(字节)FileReader/FileWriter(字符)
注意:写入到文件中时(write),文件不需要实际存在,会自动创建。

  • 流的通用操作
    1. 实例化文件对象。
    2. 创建节点流。
    3. 创建处理流(如有需求)。
    4. 具体操作。
    5. 关闭流。

处理流

缓冲流

BufferedInputStream、BufferedOutputStream/BufferedReader、BufferedWriter。

  • 作用
    提高流的读取、写入的速度。
    提高读写速度的原因:内部提供了一个缓冲区。
    使用flush方法可以强制将缓冲区的内容全部写入到输出流。

常用BufferedReader提供的readLine方法来读取一行字符。

转换流

解码(破解):字节——>字符数组、字符串
编码:字符数组——>字节
InputStreamReader:将字节的输入流转换为字符的输入流(解码)

OutputStreamWriter:将字符的输出流转换为字节的输出流(编码)

  • 作用
    字节流和字符流之间的转换,可以实现文件编码的转换。

对象流

对象的序列化
  • 序列化机制
    对象序列化机制允许把内存中的Java对象转换成二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

简单来说,序列化就是对象转换成二进制流,保存在内存中。
反序列化就是将二进制流转换为对象输出。

  • Java对象可序列化应满足的条件
    1. 实现接口:Serializable
    2. 当前类提供一个全局常量:serialVersionUID(标识作用)。
    3. 保证当前类的内部也需要实现可序列化(基本数据类型本身就是可序列化的)。
对象流的使用

ObjectInputStream(反序列化)和ObjectOutputStream(序列化)

  • 作用
    用于存储和读取基本数据类型或对象的处理流,可以把Java中的对象写入到数据中,也可以把对象从数据源中还原。
    ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量。

其他流

标准输入输出流

字节流。
System.in:标准的输入流,默认从键盘输入。
System.out:标准的输出流,默认从控制台输出。
System.exit(int status):终止当前正在运行的Java虚拟机,这个status表示退出的状态码,非零表示异常终止。(可以返回给其他进程的调用者一个调用的返回码,以根据返回码采取不同的策略。)

注意:不管status为何值程序都会退出,和return 相比有不同的是:return是回到上一层,而System.exit(status)是回到最上层。

打印流

字符流。
PrintStream、PrintWriter提供了一系列重载的print() 和 println()。

数据流

字节流。
DataInputStream 和 DataOutputStream
作用:用于读入或写出基本数据类型的变量或字符串。
注意:想要写出文件内容,不可以双击打开,应该使用DataOutputSream来写出文件。

随机存取文件流

直接继承Object类,实现了DataInput和DataOutput接口。

  • 构造器和方法
    调用RandomAccessFile构造器会传入两个参数,一个是File文件,另一个是mod
    r:只读; rw:既可以读入,也可以写出。
    方法:write():文件覆盖的操作; seek(pos):调整当前指针pos的位置。
  • 作用
    既可以输入,又可以输出的流。
    RandomAccessFile:写入文件时,对于原有文件会从头开始覆盖。如果文件不存在,会自动创建文件。可以较方便地实现文件的插入操作

NIO.2

NIO将以更加高效的方式进行文件的读写操作。
Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套是网络编程NIO。

  1. Path可以看成是File类的升级版本,实际引用的资源也可以不存在。
  2. Paths 类提供的静态 get() 方法用来获取 Path 对象。
  3. Files 用于操作文件或目录的工具类。
  • 在以前IO操作都是这样写的:
    import java.io.File;
    File file = new File(“index.html”);
  • 但在Java7 中,我们可以这样写:
    import java.nio.file.Path;
    import java.nio.file.Paths;
    Path path = Paths.get(“index.html”);

网络编程

网络编程概述

在开始介绍网络编程之前,先介绍一些基本的网络概念:
IP: 唯一的标识 Internet 上的计算机(通信实体)
IPv4:4个字节组成,如192.168.0.1。
IPv6:16个字节组成。
域名: www.baidu.com等,可以通过域名来访问IP地址。
DNS: 域名解析服务器,可以解析域名。
本地回路地址: 127.0.0.1 域名:localhost

端口号: 标识正在计算机上运行的进程(程序)。被规定一个为16位的整数。(0~65535)。不同的进程应该有不同的端口号。

Socket: 端口号和IP地址组合得出一个网络套接字。

TCP协议: 传输前:采用“三次握手”的方式,点对点通信,形成数据通道,保证可靠。
发送结束:释放已建立的链接,但是效率比较低,“四次挥手”断开链接。

UDP协议: 不需要建立连接,不可靠的链接,发送结束无需释放资源,开销小速度快。

URL: 统一资源定位符,对应着互联网的某一资源地址。
一个URL类就表示一个URL。具体方法见仓库。

  • URL格式:
    http://localhost:8080/examples/beauty.jpg?username=Tom
    协议 主机名 端口号 资源地址 参数列表

拓展:端口分类:

  • 公认端口:被预先定义的通信服务占用。比如HTTP占用端口80。(0~1023)
  • 注册端口:分配给用户进程或应用程序。比如MySQL占用端口3306。比如tomcat的端口号为8080。(1024~49151)
  • 动态/私有端口:(49152~65535)

网络编程目的:
直接或间接通过网络协议与其他计算机实现数据交换,进行通讯。

  • 如何准确定位主机,与主机上特定的应用?——通信双方地址:IP和端口号
  • 找到主机后如何可靠的进行数据传输?
    ——网络通信协议:TCP/IP参考模型

  • 通信要素1:IP和端口号
    InetAddress类的对象,就表示一个IP地址。一个IP地址标识互联网上的一台主机。
    具体方法见仓库。
  • 通信要素2:网络协议
    传输层:TCP/UDP协议。网络层:IP协议。

TCP、UDP、URL网络编程

TCP网络编程练习一:客户端发送信息给服务端,服务端将数据显示在控制台上。
客户端:

  1. 创建InetAddress对象和Socket对象,指明服务器端(现在是本地)IP地址和端口号。
  2. 使用Socket对象获取输出流。(输出流:将文字从程序输出到网络。)
  3. 关闭流。

服务端:

  1. 创建服务端ServerSocket对象,指明自己端口号。
  2. 创建Socket对象,ServerSocket对象调用accept()方法接受来自客户端的socket。
  3. 使用Socket对象获取输入流。(输入流:将网络中的信息读取到程序。)
  4. 创建输出流。(输出流:进一步将程序中的信息写入到内存中。)
    注:这里比较特殊,使用ByteArrayOutputStream流读取文字信息到内存。
  5. 关闭流。
    TCP网络编程练习二:客户端向服务端发送一个图片,并且服务端保存图片到本地。
    TCP网络编程练习三:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。
    与练习一类似,详情参见代码仓库即可。

UDP网络编程练习:客户端发送信息给服务端,服务端将数据显示在控制台上。
客户端:使用DatagramSocket的方法【send】发送DatagramPacket的packet。
服务端:使用DatagramSocket的方法【receive】接收DatagramPacket的packet。


URL网络编程练习:

  1. 创建URL对象。
  2. 打开HttpsURLConnection类的对象的链接。
  3. 使用HttpsURLConnection类的对象打开连接。
  4. 使用HttpsURLConnection类的对象获取输入流,将URL资源从网络输入到程序。
  5. 创建输出流,将URL资源输出到内存中。

Java反射机制

Java反射机制概述

  • 初识反射
    Java Reflection 被视为动态语言的关键,允许程序在执行期借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性方法(包括声明为私有的属性和方法)。
    加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。类也是一种Class的实例,可见Java中万事万物皆对象。
    体会反射的动态性:在编译时,无法确定具体所需的类的对象,只有在运行时才可以确定,这里就可以用到反射。动态,通俗点来说就是在程序运行时代码可以根据某些条件改变自身结构。
  • 与反射相关的API
    java.lang.Class :代表一个类
    java.lang.reflect.Method:代表类的方法
    java.lang.reflect.Field:代表类的成员变量
    java.lang.reflect.Constructor:代表类的构造器

获取Class类的实例以及创建运行时类的对象

  • 获取Class类的实例的三种方式
    1. 使用类名+.class
    2. 通过运行时类的对象,调用getclass()
    3. 调用Class的静态方法 forName(String classPath)
  • 创建运行时类的对象
    使用Class类的方法newInstance()可以创建运行时类的对象。其内部调用了运行时类的空参构造器。
    要想此方法正常的创建运行时类的对象,要求:
    1. 运行时类必须提供空参的构造器。
    2. 空参的构造器的访问权限足够,通常为public。

获取运行时类属性、方法

  • 获取一个运行时类的属性操作大体如下:

    1. 获取Class的实例(使用 .class)。
    2. 获取运行时类的对象(调用newInstance)。
    3. 获取指定属性(调用getDeclaredField)。
    4. 保证当前属性可访问(调用setAccessible)。
    5. 设置指定对象属性(调用set方法)。
  • 常见的第三步的其他操作:
    属性:getDeclaredField(String)—获取指定属性,String为属性名。
    方法:getDeclaredMethod(String,参数的Class实例)—获取指定方法,String为方法名。

  • 常见的第五步的其他操作:
    属性:set(obj)—设置对象属性值、get()—获取对象属性值。
    方法:invoke(obj,args)—表示给方法传参,args就是传递的参数,obj表示该方法属于哪个运行时类的对象。invoke的返回值为方法的返回值。

获取运行时类的完整结构

关于运行时类,我们可以通过一些方法来获取类的结构,比如:

  • 获取当前运行时类的属性(Field)——权限修饰符 数据类型 变量名

  • 获取当前运行时类的方法(Method)——注解 权限修饰符 返回值类型 方法名 参数类型 异常类型

  • 获取当前运行时类的构造器(Constructor)、父类(Superclass)、带泛型的父类(GenericSuperclass)的泛型(ActualTypeArauments)、接口(Interfaces)、所在的包(Package)、类声明的注解(Annotations)。

此内容仅了解即可,不要求掌握,因为在上文调用运行时类的指定结构中用到了。

ClassLoader的理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jkr2nINN-1656395185362)(en-resource://database/765:1)]

  • 类的加载
    1. 程序经过javac.exe命令编译以后,会生成一个或多个字节码文件(.class结尾,一个类就有一个.class文件)。
    2. 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。
      加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。也可以说,Class的实例就对应着一个运行时类。
  • 类的加载过程
    当程序使用某个类时,会进行以下初始化:
    类的加载——> 类的链接——> 类的初始化
    将class文件加载到内存中 ->为类变量分配内存,设置默认初始值 -> 给类变量进行赋值

类加载器的作用:把类装载进内存。
了解类加载器以及读取配置文件的两种造流方式:代码见仓库。

反射的应用:动态代理

  • 代理模式
    代理模式:代理模式是Java中使用较多的一种设计模式,代理设计就是为其他对象提供一种代理以控制对这个对象的访问,也就是通过代理类来调用其他对象的方法。
    应用场景:
    安全代理:屏蔽对真实角色的直接访问。
    远程代理:通过代理类处理远程方法调用(RMI)。
    ……
    分类:
    静态代理:专门针对某一接口来处理的代理。
    动态代理:在程序运行时根据需要动态创建目标类的代理对象。

动态代理面向切面编程(AOP)到框架的时候再深入学习。
动态代理的应用:Spring 的 AOP 、加事务、加权限、加日志。

⭐码字不易,求个关注⭐
⭐点个收藏不迷路哦~⭐

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sivan_Xin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值