Java基础知识(三)

一、泛型

1.泛型的概念

是一种在编程中用来增加代码的灵活性和安全性的机制。它允许我们在类、接口或方法的定义中使用参数化类型,即将类型作为参数传入,以便在使用时动态确定具体的类型。

2.泛型的作用

通过使用泛型,我们可以编写通用的代码,适用于多种数据类型。这样可以减少重复编写相似代码的工作量,提高代码的复用性和可维护性。

在使用泛型时,我们可以指定具体的类型参数,比如List<String>表示一个存储字符串的列表,Map<Integer, String>表示一个存储整数与字符串对应关系的映射表。通过这种方式,我们可以在编译时期进行类型检查,避免了在运行时出现类型转换错误的风险。

3.泛型的类型

① 类型参数化:通过在类或接口的定义中指定一个或多个类型参数,使得这些类型参数可以在类或接口中被使用,实现参数化的灵活性。例如,class MyClass<T>中的T就是类型参数。

// 假设有一个类Pair表示一个键值对,我们可以用泛型来表示键和值的类型
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

// 实例化
Pair<String, Integer> pair = new Pair<>("age", 18);

② 泛型方法:在方法的定义中使用泛型,使得方法的参数、返回值或局部变量可以使用泛型类型。泛型方法可以在普通类中定义,也可以在泛型类中定义。例如,public <T> T myMethod(T t)就是一个泛型方法,其中的<T>表示类型参数。

// 假设有一个类Utils,其中有一个方法printArray可以打印任意类型的数组,我们可以使用泛型方法来实现
public class Utils {
    // 泛型方法
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

// 实例化
Integer[] intArray = { 1, 2, 3, 4, 5 };
Utils.printArray(intArray);

③ 泛型类:在类的定义中使用泛型,使得类的字段、方法、构造函数等都可以使用泛型类型。泛型类可以直接使用,也可以作为父类或接口被其他类或接口继承或实现。例如,class MyClass<T>就是一个泛型类。

// 假设有一个类Box表示一个存放任意类型对象的盒子,我们可以使用泛型来表示盒子中对象的类型
public class Box<T> {
    private T object;

    public Box(T object) {
        this.object = object;
    }

    public T get() {
        return object;
    }

    public void set(T object) {
        this.object = object;
    }
}
// 实例化
Box<String> box = new Box<>("hello");
String str = box.get();

④ 通配符(Wildcard):通配符表示未知类型,可用于限制泛型的范围或限制传入的参数类型。有上界通配符(? extends T)和下界通配符(? super T)两种形式。例如,List<? extends Number>表示一个元素类型是Number或Number的子类的列表,List<? super Integer>表示一个元素类型是Integer或Integer的父类的列表。

// 假设有一个类MyList表示一个列表,其中包含一些元素,我们想要实现一个方法printList,该方法可以打印任意类型的列表中的元素,不过需要限制列表中的元素必须是某种类型或其子类
public class MyList<T> {
    private List<T> list;

    public MyList(List<T> list) {
        this.list = list;
    }

    // 打印任意类型的列表中的元素
    public void printList(List<? extends Number> list) {
        for (Number n : list) {
            System.out.print(n + " ");
        }
        System.out.println();
    }
}

// 实例化
List<Integer> intList = Arrays.asList(1, 2, 3);
MyList<Integer> myList = new MyList<>(intList);
myList.printList(intList);

二、反射

1.反射的概念

指在程序运行时动态地获取和操作类的信息,包括类名、方法名、属性等。反射机制使得Java语言具有了较高的灵活性和可扩展性,但也会增加代码的复杂性和运行效率。

2.反射的应用场景

反射机制的应用场景包括但不限于以下几个方面:

① 动态加载类:在编码过程中,如果无法确定需要使用哪个类,可以使用反射机制来动态加载类。这样可以在程序运行时根据需要动态地加载类,提高程序的灵活性和可扩展性。

② 调用对象的方法和访问对象的属性:通过反射机制,我们可以在程序运行时获取对象的方法和属性等信息,并且可以动态地调用对象的方法和访问对象的属性。这样可以在程序运行时根据需要动态地修改对象的状态,提高程序的灵活性和可扩展性。

③ 编写通用代码:通过反射机制,我们可以编写一些通用的代码,可以适用于任意类型的对象。例如,可以编写一个通用的日志记录工具,可以记录任意类型的对象的信息。

④实现注解处理器:通过反射机制,我们可以实现注解处理器,可以在编译期和运行期对注解进行解析和处理。这样可以让我们在程序中使用自定义注解来实现一些自动化操作,提高开发效率。

三、注解

1.注解的概念

是Java语言中的一种元数据(Metadata)。它是一种用来对程序代码进行标记的特殊标记符号,可以通过反射机制获取这些标记信息,并根据标记信息执行相应的操作。注解只有被解析之后才会生效。

注解在源代码中以特殊的形式出现,以@符号开头,跟在@后面的是注解类型的名称。例如,常见的注解有@Override@Deprecated@SuppressWarnings

2.注解的作用

① 提供编译时检查:通过自定义注解,可以在编译时对代码进行静态检查,发现潜在的错误或者不合规范的代码。

② 实现自动化处理:通过自定义注解处理器,在编译时或者运行时自动地对代码进行处理,生成额外的代码或者执行特定的操作。

③ 生成文档:通过自定义注解,可以为代码添加一些额外的文档信息,然后使用工具生成文档,方便其他开发人员阅读和理解代码。

④ 实现框架扩展:通过自定义注解,可以为框架提供扩展点,让用户可以通过注解来配置和定制框架的行为。

⑤ 与其他框架集成:通过使用注解,可以与其他框架进行集成,实现不同框架间的交互和协作。

3.注解的解析方法

① 编译期直接扫描:在编译Java代码时,编译器会扫描并处理注解。例如,@Override注解用于检测方法是否重写了父类的方法。编译器在编译阶段会检查标记了@Override注解的方法是否存在合法的重写关系。

② 运行期通过反射处理:一些框架中的注解(例如Spring框架中的@Value@Component等)需要在运行时通过反射机制来处理。在程序运行时,框架会使用反射读取注解信息,并根据注解中的属性值执行相应的操作,例如将带有@Value注解的字段注入对应的值,或者根据@Component注解创建对象并进行依赖注入。

四、I/O(Input/Output)

1.I/O的概念

是指计算机系统与外部环境(包括用户、设备和文件等)进行数据交换的过程。

输入(Input):指从外部环境(例如键盘、鼠标、文件等)向计算机系统传递数据的过程。

输出(Output):指将计算机系统中的数据传递给外部环境的过程。

2.常见输入输出类

在Java中,使用java.io包提供的类和接口来实现输入和输出操作。常用的输入和输出类包括:

① InputStreamOutputStream字节流输入和输出类,用于读取和写入字节数据。

② ReaderWriter字符流输入和输出类,用于读取和写入字符数据。

③ File表示文件或目录的类,用于获取文件信息、创建、删除和重命名文件等。

④ RandomAccessFile随机访问文件的类,可以读取和写入文件中的任意位置。

⑤ BufferedReaderBufferedWriter提供缓冲功能的字符流类,可以提高读写效率。

⑥ Scanner用于扫描和解析文本数据的类,可以方便地从各种输入源中读取数据。

3.Java IO流分类

① 字节流:字节为单位进行读写操作,适用于处理二进制数据或者无需考虑字符编码的情况。主要的字节流类有:InputStream(输入字节流的抽象基类,用于读取字节数据);OutputStream输出字节流的抽象基类,用于写入字节数据。具体实现类包括FileInputStreamFileOutputStreamByteArrayInputStreamByteArrayOutputStream

② 字符流:字符为单位进行读写操作,适用于处理文本数据,能够自动处理字符编码问题。主要的字符流类有:Reader输入字符流的抽象基类,用于读取字符数据);Writer输出字符流的抽象基类,用于写入字符数据)。具体实现类包括FileReaderFileWriterBufferedReaderBufferedWriter。

4.三种常见的I/O模型

BIO(Blocking I/O):同步阻塞I/O模型。在BIO模型中,每个客户端请求都会创建一个独立的线程来进行处理。当客户端连接请求到达时,服务器线程accept()方法会阻塞,直到有新的连接触发后再创建一个线程来处理该连接。BIO模型适用于连接数较少且处理时间短的情况,例如传统的Web应用程序中的HTTP请求响应。

NIO(Non-Blocking I/O):同步非阻塞I/O模型。在NIO模型中,服务器线程不再阻塞在accept()方法上,而是轮询所有连接的状态,只处理已经就绪的连接。NIO模型使用单线程就可以同时处理多个客户端连接,因此可以支持更高的并发性和吞吐量。NIO适用于需要处理大量并发连接或者需要进行高速传输的场景,例如实时通信、物联网等。

AIO(Asynchronous I/O):异步I/O模型。在AIO模型中,操作系统完成I/O操作后会通知应用程序,应用程序只需要负责数据处理即可,无需阻塞等待I/O操作的完成。AIO模型适用于需要处理大量I/O操作的场景,例如文件上传/下载、多媒体处理等。

虽然NIO相对于BIO和AIO有很大的性能优势,但是使用NIO编写网络应用程序的难度比较大,需要处理复杂的事件驱动模型。为了解决这个问题,Java 7引入了新的AsynchronousSocketChannel类,该类提供了更直接、易于使用的异步I/O方式,可以进一步提高并发和吞吐量。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tina@Qian

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

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

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

打赏作者

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

抵扣说明:

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

余额充值