再探Java为面试赋能(一)Java基础知识(一)变量初始化顺序、构造方法、clone方法

文章目录

  • 第1章 Java基础知识
    • 1.1 变量的初始化顺序
    • 1.2 构造方法
    • 1.3 clone()方法
      • 1.3.1 按值传递和按引用传递
      • 1.3.2 浅拷贝(Shallow Clone)
      • 1.3.3 深拷贝(Deep Clone)

第1章 Java基础知识

1.1 变量的初始化顺序

在Java语言中,变量一定要初始化吗?

第一,如果变量作为局部变量,也就是方法内定义的变量,或者作为方法参数的变量,在使用前一定要初始化。 例如:

public static void main(String[] args) {int a;String b;System.out.println(a);System.out.println(b);
}

执行该main()方法,控制台报错:

java: 可能尚未初始化变量a

第二,如果变量作为一个类的属性,没有初始化时,JVM 会自动把它初始化为该类型变量的默认初始值。

如int类型的变量默认初始值为0,float类型的变量默认初始值为0.0f,long 类型为0,double类型为 0.0,boolean类型为false,char类型为0(ASCII 码),所有引用类型(包括数组)的变量默认初始值为null。

在Java语言中,当要实例化一个对象时,首先要初始化所有成员变量(包括静态和非静态变量),只有当所有成员变量完成初始化后,才会调用对象所在类的构造方法创建对象。

成员变量的初始化一般遵循以下三个原则(优先级依次递减):

(1)静态变量优先于非静态变量初始化,其中静态变量只初始化一次,而非静态变量可能会初始化多次。
(2)父类优先于子类进行初始化。
(3)按照成员变量定义的顺序进行初始化,且在任意方法(包括构造方法)被调用之前先进行初始化。

成员变量的初始化工作可以在许多不同的代码块中来完成,比较常见的是静态代码块和构造方法。

静态代码块和构造方法的执行顺序是:父类静态代码块→子类静态代码块→父类非静态代码块→父类构造方法→子类非静态代码块→子类构造方法。

下面看一个例子,首先新建一个Animal类,并编写静态代码块、非静态代码块和构造方法:

public class Animal {static {System.out.println("Animal类的静态代码块执行了...");}{System.out.println("Animal类的非静态代码块执行了...");}public Animal() {System.out.println("Animal类的构造方法执行了...");}
}

在编写一个Dog类,继承Animal类,也编写静态代码块、非静态代码块和构造方法:

public class Dog extends Animal {static {System.out.println("Dog类的静态代码块执行了...");}{System.out.println("Dog类的非静态代码块执行了...");}public Dog() {System.out.println("Dog类的构造方法执行了...");}public static void main(String[] args) {new Dog();}
}

执行Dog类中的main()方法,调用Dog类的构造方法。控制台打印出来的执行顺序跟预期一致:

Animal类的静态代码块执行了...
Dog类的静态代码块执行了...
Animal类的非静态代码块执行了...
Animal类的构造方法执行了...
Dog类的非静态代码块执行了...
Dog类的构造方法执行了...

需要注意的是,静态变量在定义在直接初始化使用静态代码块进行初始化的优先级是一样的,也就是按照从上往下的顺序进行初始化。这一结论也适用于非静态变量和非静态代码块。

例如:

public class Cat {static int age = 1;static {age = 2;}static {name = "Jerry";}static String name = "Tom";public static void main(String[] args) {// 按照从上往下的顺序进行初始化// 最终age=2,name="Tom"System.out.println(age);System.out.println(name);}
}

控制台输出结果:

2
Tom

1.2 构造方法

在Java语言中,构造方法是一种特殊的方法,主要作用是完成对象的初始化工作。它具有以下特点:

  • 1)构造方法的名称必须与类的名称相同,且不能有返回值(void也不行),但可以有0个、1个或1个以上的参数。
  • 2)构造方法可以被重载(Overload),即每个类可以有多个构造方法,使用不同的参数个数或参数类型来定义多个构造方法。
  • 3)当一个类没有定义构造方法时,编译器在把源代码编译成字节码的过程中会提供一个默认的没有参数的构造方法,但该构造方法不会执行任何代码。如果定义了,则不会再创建。
  • 4)构造方法在对象实例化时会被自动调用。对于一个对象而言,只会被调用一次,而普通的方法可以被调用多次。
  • 5)构造方法不能被继承,因此不能被重写(Override),子类可以通过super关键字来显式地调用父类的构造方法。
  • 6)当父类没有提供无参数的构造方法时,子类的构造方法中必须显式地调用父类的构造方法;如果父类提供了无参数的构造方法,子类就可以不显式地调用父类的构造方法,这种情况下编译器会默认调用父类的无参数构造方法。
  • 7)在实例化对象时,会首先调用父类的构造方法,再执行子类的构造方法。
  • 8)默认构造方法的修饰符只跟当前类的修饰符有关,例如一个类被定义为public,则其构造方法也是public。

引申一个问题:普通方法是否可以与构造方法有相同的方法名?

答案是可以的。 例如:

public class Dog {public Dog() {System.out.println("Dog类的构造方法执行了...");}public void Dog() {System.out.println("Dog类的Dog()方法...");}public static void main(String[] args) {Dog dog = new Dog();dog.Dog();}
}

程序运行结果为:

Dog类的构造方法执行了...
Dog类的Dog()方法...

1.3 clone()方法

1.3.1 按值传递和按引用传递

在Java语言中,取消了C/C++语言中“指针”的概念,但实质上每个new语句返回的都是一个指针的引用,只是大部分情况下开发人员不需要关心如何去操作这个指针而已。

下面先看一个例子:

public class Book {private String name = "《Java程序员》";// getter setter ...
}
public class User {private Book book = new Book();private int age = 19;// getter setter ...public void changeBook(Book book) {book.setName("《三体1:地球往事》");}public void changeAge(int age) {age = 1;}public static void main(String[] args) {User user = new User();System.out.println("********引用类型********");System.out.println("调用changeBook()前:" + user.getBook());user.changeBook(user.getBook());System.out.println("调用changeBook()后:" + user.getBook());System.out.println("********基本数据类型********");System.out.println("调用changeAge()前:" + user.getAge());user.changeAge(user.getAge());System.out.println("调用changeAge()前:" + user.getAge());}}

上述代码的执行结果:

********引用类型********
调用changeBook()前:《Java程序员》
调用changeBook()后:《三体1:地球往事》
********基本数据类型********
调用changeAge()前:19
调用changeAge()前:19

Java在处理基本数据类型时(例如int、char、double等),都是采用按值传递的方式(传递的是输入参数的拷贝),除此之外的其他类型都是按引用传递的方式(传递的是对象的一个引用)。

因此在上述示例中,changeBook()方法的参数是Book对象的一个引用,因此修改Book对象的属性并不会影响User对象持有它;而changeAge()方法的参数真的就只是一个数,这个数怎么变跟User对象的age属性无关,除非使用this关键字进行关联,例如:

public void changeAge(int age) {// 不会修改User对象的age属性age = 1;// 会修改User对象的age属性this.age = 2;
}

对象除了在函数调用时是引用传递,在使用=赋值时也采用引用传递。 例如:

public static void main(String[] args) {Book bookA = new Book();System.out.println("bookA修改前:" + bookA.getName());// 将bookA的引用赋值给bookBBook bookB = bookA;System.out.println("bookB修改前:" + bookB.getName());// 修改bookB的信息bookB.setName("《三体1:地球往事》");// bookA的信息也会修改System.out.println("bookA修改后:" + bookA.getName());System.out.println("bookB修改后:" + bookB.getName());
}

上述代码的执行结果:

bookA修改前:《Java程序员》
bookB修改前:《Java程序员》
bookA修改后:《三体1:地球往事》
bookB修改后:《三体1:地球往事》

1.3.2 浅拷贝(Shallow Clone)

在实际编程中,经常会遇到从某个已知的对象A创建出另外一个与A具有相应状态的对象B,并且要求对B的修改不会影响到A的状态。

在Java语言中,仅通过简单的赋值操作显然无法达到这个目的,但Java提供了一个简单且有效的clone()方法来满足这个需求。

Java中所有的类都默认继承自Object类,而Object类中提供了一个clone()方法,用于返回一个Object对象的拷贝,这个拷贝是一个新的对象而不是原对象的引用。

使用clone()方法的步骤如下:

  • 1)要实现了clone()方法的类首先要实现Cloneable接口。Cloneable接口实质上只是一个标识接口,没有定义任何接口方法。
  • 2)在类中重写Object类的clone()方法,调用super.clone()方法。该方法得到的实际上是一个浅拷贝对象。
public class Book implements Cloneable {private String name = "《Java程序员》";// getter setter ...@Overrideprotected Book clone() throws CloneNotSupportedException {return (Book)super.clone();}public static void main(String[] args) throws CloneNotSupportedException {Book bookA = new Book();System.out.println("bookA修改前:" + bookA.getName());// 调用clone()方法得到一个新的对象Book bookB = bookA.clone();System.out.println("bookB修改前:" + bookB.getName());bookB.setName("《三体1:地球往事》");// bookA对象不受影响System.out.println("bookA修改后:" + bookA.getName());System.out.println("bookB修改后:" + bookB.getName());}
}

上述代码的执行结果:

bookA修改前:《Java程序员》
bookB修改前:《Java程序员》
bookA修改后:《Java程序员》
bookB修改后:《三体1:地球往事》

可见,此时对bookB对象的修改已不会影响bookA对象。

1.3.3 深拷贝(Deep Clone)

要注意的是,Java在重载clone()方法的时候也存在浅拷贝、深拷贝的问题。当类中只有一些基本的数据类型时,采用上述方法进行浅拷贝就可以了;但是当类中包含一些对象时,就需要用到深拷贝。例如,Book对象中还有一个Date类型的属性:

public class Book implements Cloneable {private String name = "《Java程序员》";private Date birthday = new Date();// getter setter ...@Overrideprotected Book clone() throws CloneNotSupportedException {return (Book)super.clone();}public static void main(String[] args) throws CloneNotSupportedException {Book bookA = new Book();System.out.println("bookA修改前:" + bookA.getName() + ", " + bookA.getBirthday());Book bookB = bookA.clone();System.out.println("bookB修改前:" + bookB.getName() + ", " + bookB.getBirthday());bookB.setName("《三体1:地球往事》");bookB.getBirthday().setMonth(5);System.out.println("bookA修改后:" + bookA.getName() + ", " + bookA.getBirthday());System.out.println("bookB修改后:" + bookB.getName() + ", " + bookB.getBirthday());}
}

上述代码的执行结果:

bookA修改前:《Java程序员》, Sun Mar 31 10:45:13 CST 2024
bookB修改前:《Java程序员》, Sun Mar 31 10:45:13 CST 2024
bookA修改后:《Java程序员》, Mon Jul 01 10:45:13 CST 2024
bookB修改后:《三体1:地球往事》, Mon Jul 01 10:45:13 CST 2024

可见,修改bookB对象的Dete类型属性时,会影响到bookA。

深拷贝的实现方法是在对象调用clone()方法完成浅拷贝后,再对非基本类型属性也调用clone()方法完成深拷贝。 例如上述示例的clone()方法改成这样:

@Override
protected Book clone() throws CloneNotSupportedException {Book book = (Book) super.clone();// 单独对Date类型的属性进行深拷贝book.setBirthday((Date) this.getBirthday().clone());return book;
}

再次执行main()方法,执行结果为:

bookA修改前:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookB修改前:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookA修改后:《Java程序员》, Sun Mar 31 10:56:09 CST 2024
bookB修改后:《三体1:地球往事》, Mon Jul 01 10:56:09 CST 2024

可见,此时bookA对象的Date属性值是不变的,也即完成了深拷贝。

总结一下,在实际编程的时候,要先检查类中有无非基本类型(即对象)的属性,如果没有,使用浅拷贝即可;如果有,则需要在完成浅拷贝后,对每一个非基本类型的属性进行深拷贝。

本节完,更多内容请查阅分类专栏:再探Java为面试赋能(持续更新中)

感兴趣的读者还可以查阅我的另外几个专栏:

  • SpringBoot源码解读与原理分析(已完结)
  • MyBatis3源码深度解析(已完结)
  • Redis从入门到精通(持续更新中)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MybatisPlus速成

MybatisPlus快速入门 快速入门入门案例常见注解常见配置 核心功能条件构造器自定义SQLService接口 扩展功能代码生成静态工具逻辑删除枚举处理器JSON处理器 插件功能分页插件通用分页实体 参考文档 mybatis-plus参考文档 全部资料链接 讲义 快速入门 入门案例 <dependency…

骑行不将就,坐垫要讲究!跟维乐来一场骑美合一的美学旅行~

想象一下&#xff0c;你胯下的坐垫不再是冷冰冰的硬疙瘩&#xff0c;而是化身为“骑行界的舒适艺术家”。美学坐垫宛如马鞍上的微型沙发&#xff0c;采用美学与人体工学的跨界联姻&#xff0c;不仅赏心悦目&#xff0c;更能温柔拥抱你的臀部。它那精妙的曲线设计&#xff0c;仿…

Pandas基本操作

import pandas as pd import numpy as np#读入csv文件 book_df pd.read_csv("./doubantushu2.csv",sep,,headerNone,names[bookname,writer,publication,year,price,value])#inplace "",表明是否对原数据库进行修改&#xff0c;默认为False&#xff08;不…

AI大模型在金融行业的应用场景和落地路径

作者&#xff1a;林建明 来源&#xff1a;IT阅读排行榜 本文摘编自《AIGC重塑金融&#xff1a;AI大模型驱动的金融变革与实践》&#xff0c;机械工业出版社出版这是最好的时代&#xff0c;也是最坏的时代。尽管大模型技术在金融领域具有巨大的应用潜力&#xff0c;但其应用也面…

minor 通过nginx代理 配置 OK

#以下勿动 location /fileStorage/upload/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_he…

EXCEL VBA限制工作数据批号或者自定义规则完整

EXCEL VBA限制工作数据批号或者自定义规则完整 Private Sub Worksheet_Change(ByVal Target As Range)Dim nRow%, Arr(), cMc$, cPc$, cTxt$, nSum!If Target.Row 1 Or Target.Column <> 4 Then Exit SubIf Target.CountLarge > 1 Then Exit SubcMc Target.Offset(0…

再说机器学习

之前我们讨论过机器学习&#xff0c;那是在大厂AI课笔记里面。 今天我们再来说说机器学习。 机器学习概念 机器学习是人工智能的一个子领域&#xff0c;它的核心是让计算机从数据中学习&#xff0c;从而能够自动地改进其性能&#xff0c;在没有明确编程的情况下能够预测新数…

基于两个单片机串行通信的电子密码锁设计

1.功能 电子号码锁在实际应用中应该有两部分&#xff0c;一部分在外部&#xff0c;有键盘部分和密码显示&#xff1b;另一部分内部&#xff0c;设置密码、显示密码。使用单片机自身带有的串口可以很方便的实现单片机之间的通信&#xff0c;使输入的密码值传送到主机检验是否是…

ctf题目

目录 1.文件包含的一道题目&#xff0c;没什么难度&#xff0c; 2.一道sql注入的题目&#xff0c;伪静态 3.限制只能本地访问。 1.文件包含的一道题目&#xff0c;没什么难度&#xff0c; 但是一个点就是它这里去包含的那个文件名就是flag&#xff0c;而不是flag.php也不是f…

数据库---------完全备份和增量备份的数据恢复,以及断点恢复

目录 一、在数据库表中&#xff0c;分三次录入学生考试成绩 1.1先创建库&#xff0c;创建表&#xff0c;完成三次数据的录入 1.2首次录入成绩后&#xff0c;做该表的完全备份 1.3第二次插入后 做增量备份 1.4第三次插入后 做增量备份 二、模拟数据丢失&#xff0c;并使用…

小练习——java中循环语句打印乘法口诀表

正向乘法表 for (int i 1; i<9; i) {for (int j 1; j < i; j) {System.out.print(j "X" i "" (i * j) "\t");}System.out.println();}代码结果&#xff1a; 打印反向乘法表 for (int i 9; i > 1; i--) {for (int j 1; j <…

P32—P33:数据类型概述

P32 数据类型的作用&#xff1a;指导虚拟机JVM在运行时给该数据分配多大的内存 java中的数据类型包括两种&#xff1a;基本数据类型和引用数据类型 基本数据类型包括四大类八种&#xff1a; 第一类&#xff1a;整数型—byte、short、int、long第二类&#xff1a;浮点型—floa…

大数据技术之 Apache Doris(一)

第 1 章 Doris 简介 1.1 Doris 概述 Apache Doris 由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018 年贡献到 Apache 社区后&#xff0c;更名为 Doris &#xff09;&#xff0c;在百度内部&#xff0c;有超过 200 个产品线在使用&#xff0c;部署机器超过 10…

机器学习周报第35期

目录 一、文献阅读&#xff1a;You Only Look Once: Unified, Real-Time Object Detection1.1 摘要1.2 背景1.3 论文模型1.4 网络设计1.5 YOLO的局限性1.6 实现代码 target 7*7*30 值域为0-1 一、文献阅读&#xff1a;You Only Look Once: Unified, Real-Time Object Detection…

从0开始搭建基于VUE的前端项目(三) Vuex的使用与配置

准备与版本 vuex 3.6.2(https://v3.vuex.vuejs.org/zh/)概念 vuex是什么? 是用作 【状态管理】的 流程图如下 state 数据状态,成员是个对象 mapState 组件使用this.$store.state.xxx获取state里面的数据 getters 成员是个函数,方便获取state里面的数据,也可以加工数据 ma…

LC 106.从中序与后序遍历序列构造二叉树

106. 从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a; inorder [9,3,15,20,7], post…

突破编程_C++_网络编程(OSI 七层模型(物理层与数据链路层))

1 OSI 七层模型概述 OSI&#xff08;Open Systems Interconnection&#xff09;七层模型&#xff0c;即开放系统互联参考模型&#xff0c;起源于 20 世纪 70 年代和 80 年代。随着计算机网络技术的快速发展和普及&#xff0c;不同厂商生产的计算机和网络设备之间的互操作性成为…

个人主页导航源码

源码简介 个人主页导航源码&#xff0c;个人主页导航源码&#xff0c;一款带后台的个人导航主页源码。 搭建环境 PHP 5.2 Nginx Mysql5.6 安装教程 1.上传源码压缩包到网站目录并解压 2.访问网站域名安装提示进行安装即可 后台路径为&#xff1a;https://域名/admin/ …

ngrok 内网穿透使用

title: ngrok 内网穿透使用 search: 2024-02-29 文章目录 背景Windows安装ngrok指令授权ngrok个人用户Authtoken穿透 http 或 https 服务ngrok的代理http指令ngrok获得静态域名指令ngrok的代理ssh指令 背景 这次寒假回家&#xff0c;很无奈&#xff0c;很多东西放在项目组服务…

3月份 月总结

抓住3月份的尾巴&#xff0c;简单写一篇月度总结。 3月份之所以产出比较少&#xff0c;是因为时间都花在学习和工作上了。 学习的内容包括但不限于 开源Grbl_Esp32 gcode学习 web串口 svg转gcode的生成 字体转svg 位图转矢量图 potrace算法 扫描线相关知识 矢量图编辑器调研 …