泛型中K T V E ? Object等分别代表什么含义。

E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定的java类型(无限制通配符类型) S、U、V – 这几个有时候也有,这些字母本身没有特定的含义,它们只是代表某种未指定的类型。一般认为和T差不多。 Object – 是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。 扩展知识 代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 // 示例1:使用T作为泛型类型参数,表示任何类型 public class MyGenericClass<T> { private T myField; public MyGenericClass(T myField) { this.myField = myField; } public T getMyField() { return myField; } } // 示例2:使用K、V作为泛型类型参数,表示键值对中的键和值的类型 public class MyMap<K, V> { private List<Entry<K, V>> entries; public MyMap() { entries = new ArrayList<>(); } public void put(K key, V value) { Entry<K, V> entry = new Entry<>(key, value); entries.add(entry); } public V get(K key) { for (Entry<K, V> entry : entries) { if (entry.getKey().equals(key)) { return entry.getValue(); } } return null; } private class Entry<K, V> { private K key; private V value; public Entry(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } } // 示例3:使用E作为泛型类型参数,表示集合中的元素类型 public class MyList<E> { private List<E> elements; public MyList() { elements = new ArrayList<>(); } public void add(E element) { elements.add(element); } public E get(int index) { return elements.get(index); } } // 示例4:使用Object作为泛型类型参数,表示可以接受任何类型 public class MyGenericClass { private Object myField; public MyGenericClass(Object myField) { this.myField = myField; } public Object getMyField() { return myField; } }

March 22, 2026 · 2 min · santu

泛型中上下界限定符extends 和 super有什么区别?

典型回答 extends <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 ```java // 定义一个泛型方法,接受任何继承自Number的类型 public void processNumber(T number) { // 在这个方法中,可以安全地调用Number的方法 double value = number.doubleValue(); // 其他操作... } ``` 举个例子,假设我们有一个基本类 `Animal` 和两个子类 `Dog` 和 `Cat`: ```plain class Animal { public void makeSound() {} } class Dog extends Animal { public void bark() {} } class Cat extends Animal { public void meow() {} } ``` 我们可以使用 `extends` 限定符来定义一个泛型方法,只允许传入 `Animal` 或其子类: ```plain public class GenericExample { // 泛型方法,类型参数 T 必须是 Animal 或 Animal 的子类 public void processAnimal(T animal) { animal.makeSound(); } public static void main(String[] args) { GenericExample example = new GenericExample(); Dog dog = new Dog(); Cat cat = new Cat(); example.processAnimal(dog); // 合法 example.processAnimal(cat); // 合法 // example.processAnimal(new String()); // 编译错误 } } ``` ### super <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object ```java // 定义一个泛型方法,接受任何类型的List,并向其中添加元素 public void addElements(List<? super T> list, T element) { list.add(element); // 其他操作... } ``` 假设我们需要定义一个方法,向一个 `List` 中插入元素,这个 `List` 的泛型类型可以是某个类或该类的父类: ```plain import java.util.List; public class GenericExample { // 泛型方法,类型参数 T 必须是 Number 或 Number 的父类 public void addNumberToList(List<? super T> list, T number) { list.add(number); } public static void main(String[] args) { GenericExample example = new GenericExample(); List numberList = new ArrayList<>(); example.addNumberToList(numberList, 10); // 合法,Integer 是 Number 的子类 example.addNumberToList(numberList, 10.5); // 合法,Double 是 Number 的子类 example.addNumberToList(numberList, "Hello"); // 编译错误,String 不是 Number 的子类 } } ``` ### PECS 原则 在使用 限定通配符的时候,需要遵守**PECS原则**,即Producer Extends, Consumer Super;上界生产,下界消费。 如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends),如上面的processNumber方法。 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super),如上面的addElements方法 > extend的时候是可读取不可写入,那为什么叫上界生产呢? > > 因为这个消费者/生产者描述的<集合>,当我们从集合读取的时候,集合是生产者。 > 如果既要存又要取,那么就不要使用任何通配符。 综合示例: ```plain import java.util.ArrayList; import java.util.List; public class GenericBoundsExample { // 使用 extends 限定符来读取泛型类型 public static

March 22, 2026 · 2 min · santu

Java中创建对象有哪些种方式

典型回答 使用new关键字 这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们还可以调用任意的构造函数(无参的和有参的)。 User user = new User(); 使用反射机制 运用反射手段,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。 1 使用Class类的newInstance方法 可以使用Class类的newInstance方法创建对象。这个newInstance方法调用无参的构造函数创建对象。 1 2 User user = (User)Class.forName("xxx.xxx.User").newInstance(); User user = User.class.newInstance(); 2 使用Constructor类的newInstance方法 和Class类的newInstance方法很像, java.lang.reflect.Constructor类里也有一个newInstance方法可以创建对象。我们可以通过这个newInstance方法调用有参数的和私有的构造函数。 1 2 Constructor constructor = User.class.getConstructor(); User user = constructor.newInstance(); 这两种newInstance方法就是大家所说的反射。事实上Class的newInstance方法内部调用Constructor的newInstance方法。 使用clone方法 无论何时我们调用一个对象的clone方法,jvm就会创建一个新的对象,将前面对象的内容全部拷贝进去。用clone方法创建对象并不会调用任何构造函数。 要使用clone方法,我们需要先实现Cloneable接口并实现其定义的clone方法。如果只实现了Cloneable接口,并没有重写clone方法的话,会默认使用Object类中的clone方法,这是一个native的方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class CloneTest implements Cloneable{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public CloneTest(String name, int age) { super(); this.name = name; this.age = age; } public static void main(String[] args) { try { CloneTest cloneTest = new CloneTest("wangql",18); CloneTest copyClone = (CloneTest) cloneTest.clone(); System.out.println("newclone:"+cloneTest.getName()); System.out.println("copyClone:"+copyClone.getName()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } 使用反序列化 当我们序列化和反序列化一个对象,jvm会给我们创建一个单独的对象。其实反序列化也是基于反射实现的。 ...

March 22, 2026 · 2 min · santu

什么是SPI,和API有啥区别

典型回答 Java 中区分 API 和 SPI,通俗的讲:API 和 SPI 都是相对的概念,他们的差别只在语义上,API 直接被应用开发人员使用,SPI 被框架扩展人员使用。 API Application Programming Interface API是一组定义了软件组件之间交互的规则和约定的接口。提供方来制定接口并完成对接口的不同实现,调用方只需要调用即可。 SPI Service Provider Interface SPI是一种扩展机制,通常用于在应用程序中提供可插拔的实现。 调用方可选择使用提供方提供的内置实现,也可以自己实现。 请记住这句话:API用于定义调用接口,而SPI用于定义和提供可插拔的实现方式。 所以说,API 是面向普通开发者的,提供一组功能,使他们可以利用一个库或框架来实现具体的功能。而SPI 是面向那些希望扩展或定制基础服务的开发者的,它定义了一种机制,让其他开发者可以提供新的实现或扩展现有的功能。 知识扩展 如何定义一个SPI 步骤1、定义一组接口 (假设是org.foo.demo.IShout),并写出接口的一个或多个实现,(假设是org.foo.demo.animal.Dog、org.foo.demo.animal.Cat)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface IShout { void shout(); } public class Cat implements IShout { @Override public void shout() { System.out.println("miao miao"); } } public class Dog implements IShout { @Override public void shout() { System.out.println("wang wang"); } } 步骤2、在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 (org.foo.demo.IShout文件),内容是要应用的实现类(这里是org.foo.demo.animal.Dog和org.foo.demo.animal.Cat,每行一个类)。 ...

March 22, 2026 · 2 min · santu

什么是反射机制?为什么反射慢?

典型回答 反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有属性和方法。 Java的反射可以: 在运行时判断任意一个对象所属的类。 在运行时判断任意一个类所具有的成员变量和方法。 在运行时任意调用一个对象的方法 在运行时构造任意一个类的对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Object obj = // ... 任意对象; Class<?> clazz = obj.getClass(); // 获取成员变量 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println("成员变量: " + field.getName()); } // 获取方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("方法: " + method.getName()); } Method method = clazz.getDeclaredMethod("methodName", // 方法参数类型...); method.setAccessible(true); // 如果方法是私有的 method.invoke(obj, // 方法参数...); // 默认构造函数 Object obj = clazz.newInstance(); // 或者使用特定的构造函数 Constructor<?> constructor = clazz.getConstructor(// 参数类型...); Object obj = constructor.newInstance(// 构造函数参数...); ...

March 22, 2026 · 1 min · santu

Java注解的作用是啥

典型回答 Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。 Java的注解,可以说是一种标识,标识一个类或者一个字段,常常是和反射,AOP结合起来使用。中间件一般会定义注解,如果某些类或字段符合条件,就执行某些能力。 ✅使用自定义注解+切面减少冗余代码,提升代码的鲁棒性 扩展知识 什么是元注解 说简单点,就是 定义其他注解的注解 。 比如Override这个注解,就不是一个元注解。而是通过元注解定义出来的。 1 2 3 4 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } 这里面的@Target,@Retention就是元注解。 元注解有四个:@Target(表示该注解可以用于什么地方)、@Retention(表示在什么级别保存该注解信息)、@Documented(将此注解包含在javadoc中)、@Inherited(允许子类继承父类中的注解)。 一般@Target是被用的最多的。 @Retention 指定被修饰的注解的生命周期,即注解在源代码、编译时还是运行时保留。它有三个可选的枚举值:SOURCE、CLASS和RUNTIME。默认为CLASS。 1 2 3 4 5 6 7 import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyRuntimeAnnotation { // some elements and values } @Target 指定被修饰的注解可以应用于的元素类型,如类、方法、字段等。这样可以限制注解的使用范围,避免错误使用。 1 2 3 4 5 6 7 import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyTargetAnnotation { // some elements and values } ...

March 22, 2026 · 1 min · santu

Java的动态代理如何实现?

典型回答 在Java中,实现动态代理有两种方式: JDK动态代理:Java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。 Cglib动态代理:Cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。 JDK动态代理和Cglib动态代理的区别: JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。 Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。 Cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。 所以,使用JDK动态代理的对象必须实现一个或多个接口;而使用cglib代理的对象则无需实现接口,达到代理类无侵入。 拓展知识 静态代理和动态代理的区别 最大的区别就是静态代理是编译期确定的,但是动态代理却是运行期确定的。 同时,使用静态代理模式需要程序员手写很多代码,这个过程是比较浪费时间和精力的。一旦需要代理的类中方法比较多,或者需要同时代理多个对象的时候,这无疑会增加很大的复杂度。 反射是动态代理的实现方式之一。 动态代理的用途 Java的动态代理,在日常开发中可能并不经常使用,但是并不代表他不重要。Java的动态代理的最主要的用途就是应用在各种框架中。因为使用动态代理可以很方便的运行期生成代理类,通过代理类可以做很多事情,比如AOP,比如过滤器、拦截器等。 在我们平时使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的拦截器都使用了动态代理功能。我们日常看到的mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部有动态代理的身影。 Spring AOP的实现方式 Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。 JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。 CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。 JDK 动态代理的代码段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class UserServiceImpl implements UserService { @Override public void add() { // TODO Auto-generated method stub System.out.println("--------------------add----------------------"); } } public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { PerformanceMonior.begin(target.getClass().getName()+"."+method.getName()); //System.out.println("-----------------begin "+method.getName()+"-----------------"); Object result = method.invoke(target, args); //System.out.println("-----------------end "+method.getName()+"-----------------"); PerformanceMonior.end(); return result; } public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); } } public static void main(String[] args) { UserService service = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(service); UserService proxy = (UserService) handler.getProxy(); proxy.add(); } Cglib动态代理的代码段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class UserServiceImpl implements UserService { @Override public void add() { // TODO Auto-generated method stub System.out.println("--------------------add----------------------"); } } public class CglibProxy implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ //设置需要创建子类的类 enhancer.setSuperclass(clazz); enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } //实现MethodInterceptor接口方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("前置代理"); //通过代理类调用父类中的方法 Object result = proxy.invokeSuper(obj, args); System.out.println("后置代理"); return result; } } public class DoCGLib { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); //通过生成子类的方式创建代理类 UserServiceImpl proxyImp = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class); proxyImp.add(); } }

March 22, 2026 · 2 min · santu

什么是序列化与反序列化

典型回答 在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中的。只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止运行,这些对象的状态也就随之而丢失了。 但是在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java的对象序列化可以帮助我们实现该功能。 对象序列化机制(object serialization)是Java语言内建的一种对象持久化方式,**通过对象序列化,可以把对象的状态保存为字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式再转换成对象。**对象序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。 所以序列化就是把Java对象序列化成字节数组的过程,反序列化就是把字节数组再转换成Java对象的过程。 扩展知识 如何进行序列化和反序列化 在Java中,只要一个类实现了java.io.Serializable接口,那么它就可以被序列化。这里先来一段代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.hollis; import java.io.Serializable; import java.util.Date; /** * Created by hollis on 16/2/2. */ public class User implements Serializable{ private String name; private int age; private Date birthday; private transient String gender; private static final long serialVersionUID = -6849794470754667710L; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", gender=" + gender + ", birthday=" + birthday + '}'; } } code 2 对User进行序列化及反序列化的Demo: ...

March 22, 2026 · 3 min · santu

Java序列化的原理是啥

典型回答 序列化是将对象转换为可传输格式的过程。 是一种数据的持久化手段。一般广泛应用于网络传输,RMI和RPC等场景中。 几乎所有的商用编程语言都有序列化的能力,不管是数据存储到硬盘,还是通过网络的微服务传输,都需要序列化能力。 在Java的序列化机制中,如果是String,枚举或者实现了Serializable接口的类,均可以通过Java的序列化机制,将类序列化为符合编码的数据流,然后通过InputStream和OutputStream将内存中的类持久化到硬盘或者网络中;同时,也可以通过反序列化机制将磁盘中的字节码再转换成内存中的类。 **如果一个类想被序列化,需要实现Serializable接口。**否则将抛出NotSerializableException异常。Serializable接口没有方法或字段,仅用于标识可序列化的语义。 自定义类通过实现Serializable接口做标识,进而在IO中实现序列化和反序列化,具体的执行路径如下: #writeObject -> #writeObject0(判断类是否是自定义类) -> #writeOrdinaryObject(区分Serializable和Externalizable) -> writeSerialData(序列化fields) -> invokeWriteObject(反射调用类自己的序列化策略) 其中,在invokeWriteObject的阶段,系统就会处理自定义类的序列化方案。 这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。 知识拓展 Serializable 和 Externalizable 接口有何不同? 类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。 当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出 NotSerializableException。 如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该实现java.io.Serializable接口。 Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。如果没有在这两个方法中定义序列化实现细节,那么序列化之后,对象内容为空。实现Externalizable接口的类必须要提供一个public的无参的构造器。 所以,实现Externalizable,并实现writeExternal()和readExternal()方法可以指定序列化哪些属性。 如果序列化后的文件或者原始类被篡改,还能被反序列化吗? ✅serialVersionUID 有何用途? 如果没定义会有什么问题? 在Java中,有哪些好的序列化框架,有什么好处 Java中常用的序列化框架: java、kryo、hessian、protostuff、gson、fastjson等。 Kryo:速度快,序列化后体积小;跨语言支持较复杂 Hessian:默认支持跨语言;效率不高 Protostuff:速度快,基于protobuf;需静态编译 Protostuff-Runtime:无需静态编译,但序列化前需预先传入schema;不支持无默认构造函数的类,反序列化时需用户自己初始化序列化后的对象,其只负责将该对象进行赋值 Java:使用方便,可序列化所有类;速度慢,占空间

March 22, 2026 · 1 min · santu

serialVersionUID 有何用途_ 如果没定义会有什么问题?

序列化是将对象的状态信息转换为可存储或传输的形式的过程。我们都知道,Java对象是保存在JVM的堆内存中的,也就是说,如果JVM堆不存在了,那么对象也就跟着消失了。 而序列化提供了一种方案,可以让你在即使JVM停机的情况下也能把对象保存下来的方案。就像我们平时用的U盘一样。 把Java对象序列化成可存储或传输的形式(如二进制流),比如保存在文件中。这样,当再次需要这个对象的时候,从文件中读取出二进制流,再从二进制流中反序列化出对象。 但是,虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致,即serialVersionUID要求一致。 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。这样做是为了保证安全,因为文件存储中的内容可能被篡改。 当实现java.io.Serializable接口的类没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,如果Class文件没有发生变化,就算再编译多次,serialVersionUID也不会变化的。但是,如果发生了变化,那么这个文件对应的serialVersionUID也就会发生变化。 基于以上原理,如果我们一个类实现了Serializable接口,但是没有定义serialVersionUID,然后序列化。在序列化之后,由于某些原因,我们对该类做了变更,重新启动应用后,我们相对之前序列化过的对象进行反序列化的话就会报错。 扩展知识 改了会怎么样 我们举个例子吧,看看如果serialVersionUID被修改了会发生什么? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class SerializableDemo1 { public static void main(String[] args) { //Initializes The Object User1 user = new User1(); user.setName("hollis"); //Write Obj to File ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("tempFile")); oos.writeObject(user); } catch (IOException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(oos); } } } class User1 implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 我们先执行以上代码,把一个User1对象写入到文件中。然后我们修改一下User1类,把serialVersionUID的值改为2L。 ...

March 22, 2026 · 2 min · santu

留言给博主