加载中...
avatar
文章
42
标签
25
分类
21
首页
Java
Spring全家桶
  • Spring
  • SpringBoot
  • SpringCloud
JVM
源码
  • Mybatis
  • HashMap
归档
其他
  • 互联网电子书汇总
  • JAVA八股文指南
  • 历史
  • 相册
关于
Logo码农StormlingJava系列(五)| 序列化、泛型、注解、反射
搜索
首页
Java
Spring全家桶
  • Spring
  • SpringBoot
  • SpringCloud
JVM
源码
  • Mybatis
  • HashMap
归档
其他
  • 互联网电子书汇总
  • JAVA八股文指南
  • 历史
  • 相册
关于

Java系列(五)| 序列化、泛型、注解、反射

发表于2021-09-19|更新于2025-01-07|Java
|总字数:2.4k|阅读时长:7分钟|浏览量:

序列化

什么是序列化?什么是反序列化?

什么是序列化,序列化就是把 Java 对象转为二进制流,方便存储和传输。

所以反序列化就是把二进制流恢复成对象。

序列化和反序列化

类比我们生活中一些大件物品的运输,运输的时候把它拆了打包,用的时候再拆包组装。

Serializable 接口有什么用?

这个接口只是一个标记,没有具体的作用,但是如果不实现这个接口,在有些序列化场景会报错,所以一般建议,创建的 JavaBean 类都实现 Serializable。

serialVersionUID 又有什么用?

serialVersionUID 就是起验证作用。

private static final long serialVersionUID = 1L;

我们经常会看到这样的代码,这个 ID 其实就是用来验证序列化的对象和反序列化对应的对象 ID 是否一致。

这个 ID 的数字其实不重要,无论是 1L 还是 IDE 自动生成的,只要序列化时候对象的 serialVersionUID 和反序列化时候对象的 serialVersionUID 一致的话就行。

如果没有显示指定 serialVersionUID ,则编译器会根据类的相关信息自动生成一个,可以认为是一个指纹。

所以如果你没有定义一个 serialVersionUID, 结果序列化一个对象之后,在反序列化之前把对象的类的结构改了,比如增加了一个成员变量,则此时的反序列化会失败。

因为类的结构变了,所以 serialVersionUID 就不一致。

Java 序列化不包含静态变量?

序列化的时候是不包含静态变量的。

如果有些变量不想序列化,怎么办?

对于不想进行序列化的变量,使用 transient关键字修饰。

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。

说说有几种序列化方式?

Java 序列化方式有很多,常见的有三种:

  • Java 对象流列化 :Java 原生序列化方法即通过 Java 原生流 (InputStream 和 OutputStream 之间的转化) 的方式进行转化,一般是对象输出流 ObjectOutputStream和对象输入流 ObjectI叩utStream。
  • Json 序列化:这个可能是我们最常用的序列化方式,Json 序列化的选择很多,一般会使用 jackson 包,通过 ObjectMapper 类来进行一些操作,比如将对象转化为 byte 数组或者将 json 串转化为对象。
  • ProtoBuff 序列化:ProtocolBuffer 是一种轻便高效的结构化数据存储格式,ProtoBuff 序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能。

泛型

Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?

什么是泛型?

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

List<Integer> list = new ArrayList<>();

list.add(12);
//这里直接添加会报错
list.add("a");
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);
//但是通过反射添加,是可以的
add.invoke(list, "kl");

System.out.println(list);

泛型一般有三种使用方式: 泛型类、泛型接口、泛型方法。

1. 泛型类:

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{

    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}

如何实例化泛型类:

Generic<Integer> genericInteger = new Generic<Integer>(123456);

2. 泛型接口 :

class GeneratorImpl<T> implements Generator<T>{
    @Override
    public T method() {
        return null;
    }
}

实现泛型接口,指定类型:

class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}

3. 泛型方法 :

public static < E > void printArray( E[] inputArray )
   {
         for ( E element : inputArray ){
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

使用:

// 创建不同类型数组:Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray  );
printArray( stringArray  );

泛型常用的通配符有哪些?

常用的通配符为:T,E,K,V,?

  • ?表示不确定的 java 类型
  • T (type) 表示具体的一个 java 类型
  • K V (key value) 分别代表 java 键值中的 Key Value
  • E (element) 代表 Element

什么是泛型擦除?

所谓的泛型擦除,官方名叫 “类型擦除”。

Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的类型信息都会被擦掉。

也就是说,在运行的时候是没有泛型的。

例如这段代码,往一群猫里放条狗:

LinkedList<Cat> cats = new LinkedList<Cat>();
LinkedList list = cats;  // 注意我在这里把范型去掉了,但是list和cats是同一个链表!
list.add(new Dog());  // 完全没问题!

因为 Java 的范型只存在于源码里,编译的时候给你静态地检查一下范型类型是否正确,而到了运行时就不检查了。上面这段代码在 JRE(Java 运行环境)看来和下面这段没区别:

LinkedList cats = new LinkedList();  // 注意:没有范型!
LinkedList list = cats;
list.add(new Dog());

为什么要类型擦除呢?

主要是为了向下兼容,因为 JDK5 之前是没有泛型的,为了让 JVM 保持向下兼容,就出了类型擦除这个策略。

注解

说一下你对注解的理解?

Java 注解本质上是一个标记,可以理解成生活中的一个人的一些小装扮,比如戴什么什么帽子,戴什么眼镜。

注解可以标记在类上、方法上、属性上等,标记自身也可以设置一些值,比如帽子颜色是绿色。

有了标记之后,我们就可以在编译或者运行阶段去识别这些标记,然后搞一些事情,这就是注解的用处。

例如我们常见的 AOP,使用注解作为切点就是运行期注解的应用;比如 lombok,就是注解在编译期的运行。

注解生命周期有三大类,分别是:

  • RetentionPolicy.SOURCE:给编译器用的,不会写入 class 文件
  • RetentionPolicy.CLASS:会写入 class 文件,在类加载阶段丢弃,也就是运行的时候就没这个信息了
  • RetentionPolicy.RUNTIME:会写入 class 文件,永久保存,可以通过反射获取注解信息

所以我上文写的是解析的时候,没写具体是解析啥,因为不同的生命周期的解析动作是不同的。

像常见的:

就是给编译器用的,编译器编译的时候检查没问题就 over 了,class 文件里面不会有 Override 这个标记。

再比如 Spring 常见的 Autowired ,就是 RUNTIME 的,所以在运行的时候可以通过反射得到注解的信息,还能拿到标记的值 required 。

反射

什么是反射?应用?原理?

什么是反射?

我们通常都是利用 new方式来创建对象实例,这可以说就是一种 “正射”,这种方式在编译时候就确定了类型信息。

而如果,我们想在时候动态地获取类信息、创建类实例、调用类方法这时候就要用到反射。

通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

反射最核心的四个类:

Java 反射相关类

反射的应用场景?

一般我们平时都是在在写业务代码,很少会接触到直接使用反射机制的场景。

但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

像 Spring 里的很多 注解 ,它真正的功能实现就是利用反射。

就像为什么我们使用 Spring 的时候 ,一个 @Component注解就声明了一个类为 Spring Bean 呢?为什么通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?

这些都是因为我们可以基于反射操作类,然后获取到类 / 属性 / 方法 / 方法的参数上的注解,注解这里就有两个作用,一是标记,我们对注解标记的类 / 属性 / 方法进行对应的处理;二是注解本身有一些信息,可以参与到处理的逻辑中。

反射的原理?

我们都知道 Java 程序的执行分为编译和运行两步,编译之后会生成字节码 (.class) 文件,JVM 进行类加载的时候,会加载字节码文件,将类型相关的所有信息加载进方法区,反射就是去获取这些信息,然后进行各种操作。

文章作者: stormling
文章链接: http://www.stormling.top/posts/10468.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 码农Stormling!
Java基础核心
cover of previous post
上一篇
Java系列(六)| JDK1.8 新特性
JDK 已经出到 17 了,但是你迭代你的版本,我用我的 8。JDK1.8 的一些新特性,当然现在也不新了,其实在工作中已经很常用了。 JDK1.8 都有哪些新特性?JDK1.8 有不少新特性,我们经常接触到的新特性如下: 接口默认方法:Java 8 允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字修饰即可 Lambda 表达式和函数式接口:Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用 Lambda 表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题,《Effective Java》作者 Josh Bloch 建议使用 Lambda 表达式最好不要超过 3 行。 Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合 Lambda 表达式可以方便的对集合进行处理。 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API...
cover of next post
下一篇
Java系列(四)| 异常处理及IO
Java 中异常处理体系?Java 的异常体系是分为多层的。 Java 异常体系 Throwable是 Java 语言中所有错误或异常的基类。 Throwable Error:系统内部错误比如虚拟机异常,是程序无法处理的。 Exception:程序问题导致的异常 CheckedException 受检异常:编译器会强制检查并要求处理的异常。 RuntimeException 运行时异常:程序运行中出现异常,比如我们熟悉的空指针、数组下标越界等等 异常的处理方式?针对异常的处理主要有两种方式: 遇到异常不进行具体处理,而是继续抛给调用者 (throw,throws) 抛出异常有三种形式,一是 throw, 一个 throws,还有一种系统自动抛异常。 throws 用在方法上,后面跟的是异常类,可以跟多个;而 throw 用在方法内,后面跟的是异常对象。 try catch 捕获异常 在 catch 语句块中补货发生的异常,并进行处理。 try { //包含可能会出现异常的代码以及声明异常的方法 }catch(Exception e)...
相关推荐
cover
2021-09-19
Java系列(四)| 异常处理及IO
Java 中异常处理体系?Java 的异常体系是分为多层的。 Java 异常体系 Throwable是 Java 语言中所有错误或异常的基类。 Throwable Error:系统内部错误比如虚拟机异常,是程序无法处理的。 Exception:程序问题导致的异常 CheckedException 受检异常:编译器会强制检查并要求处理的异常。 RuntimeException 运行时异常:程序运行中出现异常,比如我们熟悉的空指针、数组下标越界等等 异常的处理方式?针对异常的处理主要有两种方式: 遇到异常不进行具体处理,而是继续抛给调用者 (throw,throws) 抛出异常有三种形式,一是 throw, 一个 throws,还有一种系统自动抛异常。 throws 用在方法上,后面跟的是异常类,可以跟多个;而 throw 用在方法内,后面跟的是异常对象。 try catch 捕获异常 在 catch 语句块中补货发生的异常,并进行处理。 try { //包含可能会出现异常的代码以及声明异常的方法 }catch(Exception e)...
cover
2021-09-18
Java系列(三)| 常用类
StringString 是 Java 基本数据类型吗?可以被继承吗? String 是 Java 基本数据类型吗? 不是。Java 中的基本数据类型只有 8 个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type)。 String 是一个比较特殊的引用数据类型。 String 类可以继承吗? 不行。String 类使用 final 修饰,是所谓的不可变类,无法被继承。 String 和 StringBuilder、StringBuffer 的区别? String:String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。 StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。 StringBuilder:StringBuffer 的非线程安全版本,性能上更高一些。 String str1 = new String(“abc”)...
cover
2021-09-17
Java系列(二)| 面向对象
面向对象和面向过程的区别? 面向过程 :面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的一次调用就可以。 面向对象 :面向对象,把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事件在解决整个问题的过程所发生的行为。目的是为了写出通用的代码,加强代码的重用,屏蔽差异性。 用一个比喻:面向过程是编年体;面向对象是纪传体。 面向对象有哪些特性...
cover
2021-09-16
Java系列(一)| 基础语法
Java 概述什么是 Java?Java 是一门面向对象的编程语言,不仅吸收了 C++ 语言的各种优点,还摒弃了 C++ 里难以理解的多继承、指针等概念,因此 Java 语言具有功能强大和简单易用两个特征。Java 语言作为静态面向对象编程语言的优秀代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。 Java 语言有哪些特点?Java 语言有很多优秀(可吹)的特点,以下几个是比较突出的: Java 语言特点 面向对象(封装,继承,多态); 平台无关性:Java 是 “【一次编写,到处运行(Write Once,Run any Where)】” 的语言,因此采用 Java 语言编写的程序具有很好的可移植性,而保证这一点的正是 Java 的虚拟机机制。在引入虚拟机之后,Java 语言在不同的平台上运行不需要重新编译。 支持多线程:C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计 编译与解释并存; JVM、JDK 和 JRE 有什么区别?JVM:Java Virtual Machine,Java 虚拟机,Java...

评论
ValineGitalk
avatar
stormling
文章
42
标签
25
分类
21
Follow Me
公告
欢迎大家来到Stormling博客
目录
  1. 1. 序列化
    1. 1.1. 什么是序列化?什么是反序列化?
    2. 1.2. 说说有几种序列化方式?
  2. 2. 泛型
    1. 2.1. Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
  3. 3. 注解
    1. 3.1. 说一下你对注解的理解?
  4. 4. 反射
    1. 4.1. 什么是反射?应用?原理?
最新文章
面向八股文面试专场
面向八股文面试专场2025-01-22
【每日早报】-2025-01-21 - 星期二
【每日早报】-2025-01-21 - 星期二2025-01-21
规则引擎 Drools 8+ 快速入门
规则引擎 Drools 8+ 快速入门2024-12-11
数据库系列(二) | Mybatis Plus 3.0+快速入门
数据库系列(二) | Mybatis Plus 3.0+快速入门2024-12-09
分布式系列(二) | Redisson分布式锁
分布式系列(二) | Redisson分布式锁2024-12-05
©2019 - 2025 By stormling
码农Stormling程序员,关注公众号【码农Stormling】回复【面试】获取最全面试pdf
搜索
数据加载中