@@ -866,16 +866,97 @@ Object 类提供的 clone()方法可以非常简单地实现对象的浅拷贝
866
866
867
867
### 30.Java 创建对象有哪几种方式?
868
868
869
- Java 中有以下四种创建对象的方式:
869
+ ![ 三分恶面渣逆袭:Java创建对象的四种方式 ] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/javase-16.png )
870
870
871
- ![ Java创建对象的四种方式 ] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/javase-16.png )
871
+ Java 有四种创建对象的方式:
872
872
873
- - new 创建新对象
874
- - 通过反射机制
875
- - 采用 clone 机制
876
- - 通过序列化机制
873
+ ①、new 关键字创建,这是最常见和直接的方式,通过调用类的构造方法来创建对象。
877
874
878
- 前两者都需要显式地调用构造方法。对于 clone 机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在 Java 中序列化可以通过实现 Externalizable 或者 Serializable 来实现。
875
+ ``` java
876
+ Person person = new Person ();
877
+ ```
878
+
879
+ ②、反射机制创建,反射机制允许在运行时创建对象,并且可以访问类的私有成员,在框架和工具类中比较常见。
880
+
881
+ ``` java
882
+ Class clazz = Class . forName(" Person" );
883
+ Person person = (Person ) clazz. newInstance();
884
+ ```
885
+
886
+ ③、clone 拷贝创建,通过 clone 方法创建对象,需要实现 Cloneable 接口并重写 clone 方法。
887
+
888
+ ``` java
889
+ Person person = new Person ();
890
+ Person person2 = (Person ) person. clone();
891
+ ```
892
+
893
+ ④、序列化机制创建,通过序列化将对象转换为字节流,再通过反序列化从字节流中恢复对象。需要实现 Serializable 接口。
894
+
895
+ ``` java
896
+ Person person = new Person ();
897
+ ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream (" person.txt" ));
898
+ oos. writeObject(person);
899
+ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (" person.txt" ));
900
+ Person person2 = (Person ) ois. readObject();
901
+ ```
902
+
903
+
904
+ #### new子类的时候,子类和父类静态代码块,构造方法的执行顺序
905
+
906
+ 在 Java 中,当创建一个子类对象时,子类和父类的静态代码块、构造方法的执行顺序遵循一定的规则。这些规则主要包括以下几个步骤:
907
+
908
+ 1 . 首先执行父类的静态代码块(仅在类第一次加载时执行)。
909
+ 2 . 接着执行子类的静态代码块(仅在类第一次加载时执行)。
910
+ 3 . 再执行父类的构造方法。
911
+ 4 . 最后执行子类的构造方法。
912
+
913
+ 下面是一个详细的代码示例:
914
+
915
+ ``` java
916
+ class Parent {
917
+ // 父类静态代码块
918
+ static {
919
+ System . out. println(" 父类静态代码块" );
920
+ }
921
+
922
+ // 父类构造方法
923
+ public Parent () {
924
+ System . out. println(" 父类构造方法" );
925
+ }
926
+ }
927
+
928
+ class Child extends Parent {
929
+ // 子类静态代码块
930
+ static {
931
+ System . out. println(" 子类静态代码块" );
932
+ }
933
+
934
+ // 子类构造方法
935
+ public Child () {
936
+ System . out. println(" 子类构造方法" );
937
+ }
938
+ }
939
+
940
+ public class Main {
941
+ public static void main (String [] args ) {
942
+ new Child ();
943
+ }
944
+ }
945
+ ```
946
+
947
+ 执行上述代码时,输出结果如下:
948
+
949
+ ```
950
+ 父类静态代码块
951
+ 子类静态代码块
952
+ 父类构造方法
953
+ 子类构造方法
954
+ ```
955
+
956
+ - 静态代码块:在类加载时执行,仅执行一次,按父类-子类的顺序执行。
957
+ - 构造方法:在每次创建对象时执行,按父类-子类的顺序执行,先初始化块后构造方法。
958
+
959
+ > 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的京东面经同学 2 后端面试原题:new子类的时候,子类和父类静态代码块,构造方法的执行顺序
879
960
880
961
GitHub 上标星 10000+ 的开源知识库《[ 二哥的 Java 进阶之路] ( https://github.com/itwanger/toBeBetterJavaer ) 》第一版 PDF 终于来了!包括 Java 基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM 等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[ 太赞了,GitHub 上标星 10000+ 的 Java 教程] ( https://javabetter.cn/overview/ )
881
962
@@ -1715,45 +1796,63 @@ while (!result.isDone()) {
1715
1796
1716
1797
### 45.什么是序列化?什么是反序列化?
1717
1798
1718
- 什么是序列化,序列化就是 ** 把 Java 对象转为二进制流 ** ,方便存储和传输 。
1799
+ 序列化(Serialization)是指将对象转换为字节流的过程,以便能够将该对象保存到文件、数据库,或者进行网络传输 。
1719
1800
1720
- 所以 ** 反序列化就是把二进制流恢复成对象 ** 。
1801
+ 反序列化(Deserialization)就是将字节流转换回对象的过程,以便构建原始对象 。
1721
1802
1722
- ![ 序列化和反序列化] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/javase-30.png )
1803
+ ![ 三分恶面渣逆袭: 序列化和反序列化] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/javase-30.png )
1723
1804
1724
- 类比我们生活中一些大件物品的运输,运输的时候把它拆了打包,用的时候再拆包组装。
1805
+ #### Serializable 接口有什么用?
1725
1806
1726
- > Serializable 接口有什么用?
1807
+ ` Serializable ` 接口用于标记一个类可以被序列化。
1727
1808
1728
- 这个接口只是一个标记,没有具体的作用,但是如果不实现这个接口,在有些序列化场景会报错,所以一般建议,创建的 JavaBean 类都实现 Serializable。
1809
+ ``` java
1810
+ public class Person implements Serializable {
1811
+ private String name;
1812
+ private int age;
1813
+ // 省略 getter 和 setter 方法
1814
+ }
1815
+ ```
1729
1816
1730
- > serialVersionUID 又有什么用 ?
1817
+ #### serialVersionUID 有什么用 ?
1731
1818
1732
- serialVersionUID 就是起验证作用 。
1819
+ serialVersionUID 是 Java 序列化机制中用于标识类版本的唯一标识符。它的作用是确保在序列化和反序列化过程中,类的版本是兼容的 。
1733
1820
1734
1821
``` java
1735
- private static final long serialVersionUID = 1L ;
1736
- ```
1822
+ import java.io.Serializable ;
1737
1823
1738
- 我们经常会看到这样的代码,这个 ID 其实就是用来验证序列化的对象和反序列化对应的对象 ID 是否一致。
1824
+ public class MyClass implements Serializable {
1825
+ private static final long serialVersionUID = 1L ;
1826
+ private String name;
1827
+ private int age;
1739
1828
1740
- 这个 ID 的数字其实不重要,无论是 1L 还是 IDE 自动生成的,只要序列化时候对象的 serialVersionUID 和反序列化时候对象的 serialVersionUID 一致的话就行。
1829
+ // getters and setters
1830
+ }
1831
+ ```
1741
1832
1742
- 如果没有显示指定 serialVersionUID ,则编译器会根据类的相关信息自动生成一个,可以认为是一个指纹 。
1833
+ serialVersionUID 被设置为 1L 是一种比较省事的做法,也可以使用 Intellij IDEA 进行自动生成 。
1743
1834
1744
- 所以如果你没有定义一个 serialVersionUID, 结果序列化一个对象之后,在反序列化之前把对象的类的结构改了,比如增加了一个成员变量,则此时的反序列化会失败 。
1835
+ 但只要 serialVersionUID 在序列化和反序列化过程中保持一致,就不会出现问题 。
1745
1836
1746
- 因为类的结构变了,所以 serialVersionUID 就不一致 。
1837
+ 如果不显式声明 serialVersionUID,Java 运行时会根据类的详细信息自动生成一个 serialVersionUID。那么当类的结构发生变化时,自动生成的 serialVersionUID 就会发生变化,导致反序列化失败 。
1747
1838
1748
- > Java 序列化不包含静态变量 ?
1839
+ #### Java 序列化不包含静态变量吗 ?
1749
1840
1750
- 序列化的时候是不包含静态变量的 。
1841
+ 是的,序列化机制只会保存对象的状态,而静态变量属于类的状态,不属于对象的状态 。
1751
1842
1752
- > 如果有些变量不想序列化,怎么办?
1843
+ #### 如果有些变量不想序列化,怎么办?
1753
1844
1754
- 对于不想进行序列化的变量,使用` transient ` 关键字修饰。
1845
+ 可以使用` transient ` 关键字修饰不想序列化的变量。
1846
+
1847
+ ``` java
1848
+ public class Person implements Serializable {
1849
+ private String name;
1850
+ private transient int age;
1851
+ // 省略 getter 和 setter 方法
1852
+ }
1853
+ ```
1755
1854
1756
- ` transient ` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 ` transient ` 修饰的变量值不会被持久化和恢复。 ` transient ` 只能修饰变量,不能修饰类和方法。
1855
+ > 1 . [ Java 面试指南(付费) ] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的京东面经同学 2 后端面试原题:用过序列化和反序列化吗?
1757
1856
1758
1857
### 46.说说有几种序列化方式?
1759
1858
0 commit comments