Java学习笔记

学习自https://www.bilibili.com/video/BV1Fv4y1q7ZH?vd_source=a67a69fa2d51203b8c6f8ebbc5bfe21a&p=2&spm_id_from=333.788.player.switch

基础篇

Day1

计算机简介

计算机简介
计算机发展史

第一台通用计算机:ENIAC 用于美国国防部的弹道计算

计算机的目的:对数据进行处理和运算

数据的存储和运算

计算机中对于数据的存储和运算,都是通过二进制数据来完成

二进制

二进制:类比十进制的逢十进一,借一当十,二进制是逢二进一,借一当二

二进制的出现原因:计算机底层都是硬件电路,可以通过不通电和通电表达。而二进制中只有两个数字0和1,可以分别用来表示不通电和通电两种状态。所以二进制可以更好的配合计算机硬件去工作。

补充知识:数据的存储单位

字节(byte):一个字节由八个二进制数字组成

位(bit):字节中每个二进制位的名称,也称为比特位

人机交互方式——常用DOS命令
人机交互方式
  • 图形化界面
  • 命令行

图形化界面的弊端:消耗内存、运行速度慢

DOS命令行

就是终端啦

  • 盘符切换

    E:加回车表示切换到E盘

  • 呈现当前路径下的内容

    dir

  • 进入目录

    cd 文件夹路径

    注意:可以进入多级目录

  • 回到上一级目录

    ·cd..·

  • 回退到盘符目录

    cd\

  • 清屏

    cls

    注意:清屏无法回退

  • 退出DOS命令行(命令提示符窗口、终端)

    exit

Path环境变量
学习路径
  1. 如何在命令行中启动软件
  2. 配置Path环境变量
如何在命令行中启动软件

命令行中进入软件所在路径,直接输入软件名称.exe或者软件名称后回车,即可启动软件。

可省略的原因:.exe文件输入可执行性文件,无需输入后缀即可启动。可在命令行中输入set pathext查看可执行性文件的后缀。

细节:如果可执行文件的名称相同,且都用省略后缀的形式启动软件,会有优先循序。优先顺序为用set pathext查看的从左到右优先度递减的顺序。

配置Path环境变量

可以解决的问题使用命令行启动软件过程太过繁琐的问题。

  • path环境变量:可以理解为系统的管家,帮助用户记录了很多软件的完整路径

当我们要运行一个程序,而没有告诉它程序所在的完整路径时,系统除了在当前目录下寻找此程序外,还会到Path中指定的路径去找

即,通过path,就可以在任何位置启动我们常用的软件,简便打开开发软件的过程

配置过程:

  1. 此电脑右键点击属性
  2. 页面中打开高级系统设置
  3. 打开的系统属性页面的高级页面中点击环境变量
  4. 在系统变量中找到Path,选中并编辑
  5. 在编辑环境变量的界面即可管理Path中的应用

注意:不可以变更系统自带的Path路径!!!

计算机语言

计算机语言:人与计算机沟通交流的表达方式

发展进程
  • 机器语言

    机器语言就是0/1代码。计算机智能识别0和1.

    在计算机内部,无论什么数据,最终保存的都是0/1代码。

  • 汇编语言

    将一串机器语言转化为英文单词

  • 高级语言

    使用普通英语进行编写代码,然后通过编译器翻译成类似于汇编语言的指令,之后再由计算机执行

Java介绍与环境搭建

Java背景介绍
历史:

最初的名字:oak(橡树)

现在的名字:JAVA(咖啡的盛产地)

JAVA语言是美国Sun公司在1995年推出的计算机语言,后被Oracle公司收购

JAVA之父:詹姆斯高斯林

JAVA三大平台
  • Java SE

    java语言的标准版,用于桌面应用的开发,是其他两个版本的基础

    桌面应用:用户只要打开程序,程序的界面会让用户在最短的时间内找到他们需要的功能,同时主动带领用户完成他们的工作并得到最好的体验

    学习的目的:为今后从事JAVA EE开发打基础

  • Java ME

    java语言的小型版,用于嵌入式消费类电子设备

    退出历史舞台

  • Java EE

    java语言的企业版,用于Web方向的网站开发

    网页:用于数据展示

    网站:网页+后台服务器

Java跨平台工作原理
平台与跨平台

平台:操作系统

  • Windows
  • MacOS
  • Linux

跨平台:Java程序可以在任意操作系统上运行

跨平台原理

JAVA运行在JVM虚拟机上,只要在需要运行Java应用程序的操作系统上,安装一个与操作系统对应的Java虚拟机(JVM Java Virtual Machine)即可

JDK下载和安装
下载JDK

三种长期支持版本(LTS,即long-term support)

  • Java8.0

    企业普遍使用,兼容性好,普适性强

  • Java11.0

  • Java17.0

  • 至2024年为Java21.0

HelloWorld程序
java开发的三个步骤
  1. 编写代码

    新建后缀为.java的文档,用记事本编写文档

  2. 编译代码

    JDK的几种工具在bin文件夹里

  • javac.exe

    编译工具

  • java.exe

    运行工具

这两种工具都要在命令行中使用

将编写的文档放在bin目录中,使用javac 文档名.java编译文件,生成.class文件

  1. 运行代码

    生成.class字节码文件后,输入java 文件名运行代码

    注意:不要带.class

注意:这个.class文件是有用的,以后打包程序的时候会用到,所以不要跳过编译这步直接运行.java文件。(也不告诉你怎么直接运行嘿嘿嘿)

HelloWorld案例详解
1
2
3
4
5
public class HelloWorld {			
public static void main(String[] args) {
System.out.println(" HelloWorld ");
}
}

类名称通常与文件名称相同

HelloWorld案例常见问题
  1. windows扩展名没有勾选
  2. 没保存
  3. 文件名与类名不一致
  4. 大小写错误。单词拼写错误,存在中文符号
  5. 括号不配对
  6. 编译、执行使用不当
JDK的组成和配置JAVA_HOME
JDK的组成

JDK由JVM、核心类库、开发工具组成

  • JVM(Java Virtual Machine):JDK最核心的组成部分——JVM,它是Java虚拟机,真正运行Java程序的地方。
  • 核心类库:是Java本身写好的一些程序,给程序员调用的。 Java程序员并不是凭空开始写代码,是要基于核心类库提供的一些基础代码,进行编程。
  • JRE(Java Runtime Enviroment):意思是Java的运行环境;它是由JVM和核心类库组成的;如果你不是开发人员,只需要在电脑上安装JRE就可以运行Java程序。
  • 开发工具:Java程序员写好源代码之后,需要编译成字节码,这里会提供一个编译工具叫做javac.exe,编写好源代码之后,想要把class文件加载到内存中运行,这里需要用到运行工具java.exe。
    除了编译工具和运行工具,还有一些其他的反编译工具、文档工具等等..
JAVA_HOME配置

默认配置的工具不全

跟着视频配就好了,没啥记的必要

https://www.bilibili.com/video/BV1Fv4y1q7ZH?vd_source=a67a69fa2d51203b8c6f8ebbc5bfe21a&spm_id_from=333.788.player.switch&p=13

9:40

注意:win8后环境变量配置可能会在重启后失效,需要回到Path环境配置页面重新点一遍确认

IDEA开发工具

IDEA概述

集成开发环境(Integrated Development Environment ,简称IDE),适用于Java语言开发的集成环境,是业界公认的目前用于Java程序开发最好的工具。

集成环境:它能够将写代码、编译、运行等工具集成到一起。

除此之外,IDEA还有代码提示、检查代码错误等功能,从而提高程序员的开发效率。

IDE有很多种,常见的Eclipse、MyEclipse、Intellij IDEA、JBuilder、NetBeans等。但是这些IDE中目前比较火的是Intellij IDEA(以下简称IDEA),被众多Java程序员视为最好用的Java集成开发环境。

IDE下载和安装

没什么记得

IDEA中的第一个代码
IDEA项目结构介绍
  • project(项目、工程)
  • module(模块)
  • package(包)
  • class(类)

前三个玩应本质都是文件夹,为了能更好管理类文件而设计

类必须创建在src文件夹里!!!

IDEA运行细节和基本设置
自动编译

编译后产生的.class文件在项目文件夹中的out文件夹

IDEA中类、包、模块、项目相关操作
IDEA中的类操作
  • 新建类文件

  • 删除类文件

    这个删除是直接粉碎,莫得回收,不要删错了

  • 修改类文件

IDEA中的包操作
  • 新建包

    在src文件夹中右键新建软件包

    包的命名一般问公司网址倒着写,如:

    com.itheima.项目名(.为文件夹的层级分割,可以在页面中右键勾选外观中的压缩空的中间软件包来使层级关系更明显)

IDEA中的模块操作
  • 新建模块

    要在文件页面选择项目结构来创建

  • 删除模块

    右键一个模块是没有删除选项的,选择移除(remove),再到模块所在文件夹删除模块

  • 导入模块

    跟创建模块在一个地方

  • 修改模块

    右键重构模块有重命名,并选择同时修改

IDEA中的项目操作
  • 关闭项目

    文件中有关闭项目的选项

  • 打开项目

    傻子都会了)

  • 删除项目

    主页面列表中对项目进行remove

  • 修改项目

    选择文件中的项目结构

    编译版本要和JDK版本保持一致

    注意:编译器输出目录不要更改

    修改项目名称的话不能在项目结构改完就不管了,以下是具体操作

    1. 关闭项目
    2. 移除项目
    3. 在项目所在文件夹中更改项目文件夹的名字
    4. 重新打开
  • 新建项目

    在列表界面中重新新建项目

    注意:新建项目时的路径一定要把\改成一个\(新版本好像莫得这个问题了)

JAVA基础语法

注释
注释介绍

注释是解释说明程序的问题,方便自己和别人阅读代码

注释格式
  • 单行注释

    格式:// 注释信息

  • 多行注释

    格式

    1
    2
    3
    4
    /*
    注释信息1
    注释信息2
    */
  • 文档注释

  • 格式

    1
    2
    3
    4
    /**
    注释信息1
    注释信息2
    */

    暂时用不上

注意:被注释内容不会参与编辑与运行

快捷键

Ctrl + / 单行注释(对当前行进行注释)
Ctrl + Shift + / 对选中的代码进行多行注释。

关键字

关键字是java语言中有特殊含义的单词。比如用int表示整数,用double表示小数,等等!

注意:关键字是java用了的,我们就不能用来为做为:类名、变量名,否则会报错!

字面量

数据在程序中的书写格式

Day2

JAVA基础语法

变量

内存中的存储空间,空间中存储着经常发生改变的数据

定义形式:

数据类型 变量名 = 数据值;

细节:阅读代码中的等号最好从右往左读

Debug调试工具

Debug:是供程序员使用的程序调试工具,可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序

操作

基本操作与codeblock的调试相同(byd那个codeblock的调试环境是真难配),记一下不同的操作

  • 批量删除断点

    调试页面有一个查看断点的选项,点开可以批量管理断点

  • 添加多个断点

    调试页面有个恢复程序的按钮,可以让当前断点直接运行到下一个断点

变量的注意事项
  1. 变量名不允许重复定义
  2. 一条语句可以定义出多个变量,中间需要用逗号分隔
  3. 变量在使用之前必须完成赋值
  4. 变量有作用域

细节:IDEA为注释提供了一种特殊的标记,当在注释中输入TODO:时,进度条会有特殊标识

标识符

标志符就是名字,我们写程序时会起一些名字,如类名、变量名等等都是标识符。

命名规则

​ 1.字母、数字、下划线、$组成
​ 2.不能以数字开头
​ 3.不能是Java的关键字

命名规范
  1. 大驼峰命名法:类

    所有单词的首字母大写

  2. 小驼峰命名法:变量

    首个单词的首字母小写,其余的首字母大写

  3. 见名知意,便于自己和别人阅读

    就是写拼音

数据类型

数据类型有两类

  • 基本数据类型
  • 引用数据类型(Day5学)
基本数据类型介绍

前两列需要记住,第三列需要记住红字

定义思路
  1. 整数:首选int,装不下了用long类型

    定义long类型的时候要在数字后加一个L

  2. 小数:首选double(计算更准确),用float类型时,数字后要加F

细节

  1. 所有直接输出的整数默认是int类型,小数是double类型

  2. ASCLL编码表

    是计算机中字节到字符的一套对应关系

    所有字符在计算机底层都会有一个数值的表示形式

Scanner键盘录入
键盘录入效果介绍

可以让程序中使用的数据更加灵活

键盘录入的三个步骤
  1. 在class类上导包

    import java.util.Scanner

  2. 得到一个用于键盘扫描器对象

    1
    2
    3
    4
    //Scanner是键盘扫描器对象(你就把它理解成一个东西),这个东西有录入的功能
    //sc是给这个东西取的名字,只有sc可以变,别的都不能变
    Scanner sc = new Scanner(System.in);
    // 写的时候直接写Sca+回车就行
  3. 开始调用sc的功能,来接收用户键盘输入的数据。

    1
    2
    3
    4
    5
    6
    7
    8
    //sc这个东西有键盘录入整数的功能,这个功能的名字叫nextInt()
    //.表示表示调用的意思
    int age = sc.nextInt();
    System.out.println("我的年龄是:"+age);

    //sc这个东西还有键盘录入字符串的功能,这个功能的名字叫next
    String name = sc.next();
    System.out.println("我的姓名是:"+name);

在IDEA中使用时,导包操作会自动进行,只要在方法中输入Sca+回车即可快捷导包,剩下的第二第三步需要自行输入

运算符

算术运算符
运算符和表达式
  • 运算符

    对字面量或者变量进行操作的符号

  • 表达式

    用运算符把自变量或者变量连接起来符合java语法的式子就可以称为表达式

    不同运算符连接的表达式体现的是不同类型的表达式

算术运算符

注意:

  • 相除想要得到小数,运算过程中需要有浮点数(小数)参与运算
  • 取余可以用到一些算法上
字符串拼接操作

当+操作中,遇到了字符串,这是+就是字符串连接符,而不是算术运算

如例:

1
System.out.println("年龄为:"+23);

结果为:

1
年龄为:23

默认算术表达式从左到右进行,故下行代码被依次转化为字符串

如例:

1
System.out.println("年龄为:"+23+1);

结果为:

1
年龄为231

可以用()改变算术表达式的顺序

如例:

1
System.out.println("年龄为:" + ( 23 + 1 ) );

结果为

1
年龄为24
自增自减运算符
符号
符号 作用 说明
++ 自增 变量自身的值+1
自减 变量自身的值-1
使用方法
  1. 单独使用

    一句代码中只做自增自减,符号放变量前后无差别

  2. 参与运算使用

    符号在前,先对变量本身进行自增自减,后参与运算

    符号灾后,先参与运算,后对变量本身进行自增自减

注意:自增自减只能对变量进行操作,不能对常量操作

细节

多行注释左侧有个小减号,可以折叠

类型转换
类型转换分类
  • 隐式转换

    数据范围小的变量可以直接赋值给数据范围大的变量

    数据从小到大排列

    byte<short=char<int<long<float<double

    虽然float是四个字节,long是八个字节,但是小数的二进制存储形式更加节省内存,可表示的数据范围更大

    原理:

    自动类型转换其本质就是在较小数据类型数据前面,补了若干个字节

    Day2.3

    • 运算过程中的隐式转换

      取值范围小的数据,和取值范围大的数据进行运算,小的会先提升为大的之后,再进行运算

      注意:byte short char三种数据在运算的时候,都会自动提升为int,然后再进行运算

      示例:

      1
      2
      3
      4
      5
      6
       public class OperationDemo {
      public static void main(String[] args) {
      byte a = 10,b = 20;
      byte c = a + b; //这行直接爆红
      }
      }

      把c的类型定义为int即可

  • 强制转换

    强行将范围大的数据,赋值给范围小的变量

    格式:

    1
    2
    3
    4
    5
    6
    7
    public class OperationDemo {
    public static void main(String[] args) {
    double a = 12.3;
    int b = (int) a;
    System.out.println(b);
    }
    }

    结果为

    12

    .3没有了,我们称之为精度损失

进制
计算机中进制的分类
  • 二进制

  • 十进制

  • 八进制

    类比前两者

  • 十六进制

    用数字0到9和字母A到F(或a到f)表示10到15

不同进制的书写

对于数字110的不同进制的书写

  • 二进制

    0b110即0b开头

  • 十进制

    110

  • 八进制

    0110即0开头

  • 十六进制

    0x110即0x开头

细节:

以后编写数值的时候尽量不要以0开头

二进制到十进制的转换
  • 公式:系数*基数的权次幂 相加

    系数:就是每一位上的数

    基数:当前的进制数

    权:从右往左,依次为0 1 2 3 4 5

    示例:

  • 8421快速转换法

    二进制中,每一位的1都是代表一个固定数值

    把每一位的1代表的十进制数加起来得到的结果就是它所代表的十进制数

    示例:

原码反码补码
  • 原码

    弊端:遇到负数运算时,会出现错误

    所以计算机做运算时,都是以二进制补码的形式在运算

  • 反码

    正数的反码与其原码相同

    负数的反码是对其原码逐位取反,但符号位除外

  • 补码

    正数的补码与其原码相同

    负数的补码是在其反码的末位加1

强转中的精度损失

补码转原码,同样是取反码再给末尾加1

Day3

运算符

赋值运算符
基本赋值运算符

=

注意:

例:

1
2
3
4
public static void main(String[] args) {
double test = 5/2;
System.out.println(test);
}

结果:

2.0

赋值运算符从右向左赋值,.class文件中如5/2的运算都会直接变为运算结果,上例如下:

1
2
3
4
public static void main(String[] args) {
double test = (double)2.0F;
System.out.println(test);
}

故需要在算数式中加上小数的运算,如:

1
2
3
4
public static void main(String[] args) {
double test = (5*1.0)/2;
System.out.println(test);
}

结果为:

2.5

拓展赋值运算符

细节:拓展赋值运算符内部自带强转效果,如下

1
2
short s = 1;
s += 1;
1
2
short s = 1;
s = s + 1;

下面的代码会报错,原因与面试第一题相同,而上面的代码没有报错,说明其内部自带强转效果,真正等效的代码如下

1
2
short s = 1;
s = (short)(s + 1);
关系运算符(比较运算符)

逻辑运算符
定义

通过连接布尔类型的表达式,或者是值,来整合多个条件为一段整体逻辑的运算符

分类

三元运算符
三元运算符介绍
  • 格式:判断条件? 值1 : 值2;

  • 执行流程:

    首先计算关系表达式的值

    如果关系表达式的值为true,则返回值1;

    如果关系表达式的值为false, 则返回值2;

  • 总结:
    根据判断条件,从两份数据中二者选其一

例子

输入三个数,求最大数

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Scanner;

public class 三个数中取最大数 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int v1 = sc.nextInt();
int v2 = sc.nextInt();
int v3 = sc.nextInt();
int tempmax = v1 > v2 ? v1 : v2;
int max = tempmax > v3 ? tempmax : v3;
System.out.println("最大值为"+max);
}
}
运算符优先级

不用背,直接熟练使用小括号即可

注意:要被与的优先级大于或,短路与的优先级大于短路或

面试题
类型转换
  • 题目:下列代码是否存在错误,如果有,请指出说明,并改正

    1
    2
    3
    byte b1 = 3;
    byte b2 = 4;
    byte b3 = b1 + b2;
  • 答案:

    错误,原因在于b1和b2是两个byte类型,运算中自动提升为int类型,而b3在定义中是将int类型的结果赋值给byte类型的变量,属于大给小,不能直接给,会损失精度

    1
    2
    3
    4
    byte b1 = 3;
    byte b2 = 4;
    byte b3 = (byte)(b1 + b2);
    //先计算结果,在对结果进行强制类型转换
  • 题目:下列代码是否存在错误,如果有,请指出说明,并改正

    1
    byte b = 3 + 4;
  • 无错误,Java存在常量优化机制(在编译的时候javac会将3和4两个字面量进行运算),即产生的字节码文件中的代码为:

    1
    byte b = 7;
  • 可以通过将class文件反向编译为原文件验证其常量优化机制,直接将class文件拖入IDEA中即可查看

方法(method)

方法介绍

方法是一种语法结构,它可以把一段代码封装成一个功能,以便重复调用或独立调用。

方法的出现:

  • 可以将挤在一起的臃肿代码,按照功能进行分类管理
  • 可以提高代码的复用性
方法的定义格式和调用格式
快速上手
  • 定义格式

    1
    2
    3
    public static void 方法名() {
    // 方法体
    }
  • 调用格式

    1
    方法名();

注意:

  1. 方法与方法之间是平级关系,不允许嵌套定义
  2. 方法不调用就不执行
  3. 方法的定义顺序与执行顺序无关

细节:CTRL+F12可以展开IDEA中的代码结构

Debug查看方法的执行流程

Debug查看方法的运行过程需要按”步入”按钮(快捷键F7),步出(shift+F7)可以执行完当前所处的方法,回到主方法

方法调用内存图解
  • 方法没有被调用时,在方法区的字节码文件中存放
  • 方法被调用时,需要进入到栈内存中运行

方法区:字节码文件加载时进入的内存(存放所有字节码文件中的方法)

栈内存:方法运行时所进入的内存,有类似弹匣的特点,方法执行时按照执行顺序依次进入栈内存,执行完成后按照顺序被当作子弹打出弹匣,即栈内存,进入进出的操作称为”进栈、弹栈”

细节:主方法是程序的入口,程序执行时主方法自动第一个进入栈内存

带参数方法的定义和调用
  • 定义格式

    1
    2
    3
    public static void 方法名 (数据类型 变量名,数据类型 变量名){
    // 方法体
    }
  • 调用格式

    1
    方法名(参数1,参数2);
形参和实参

形参:全称形式参数,是指在定义方法是,所声明的函数

实参:全称实际参数,调用方法时,所传入的参数

例子:

1
2
3
4
5
6
7
8
9
10
package com.itheima.method;

public class MethodDemo2 {
public static void main(String[] args) {
Text(10); //10为实参
}

public static void Text(int Xingcan) { //此为形参
}
}
带返回值方法的定义和调用
  • 定义格式

    1
    2
    3
    public static 数据类型 方法名 (数据类型 变量名1,数据类型 变量名2...){
    return 数据值;
    }
  • 调用格式

    需要将返回的值用变量储存

    1
    数据类型 变量名 = 方法名(参数...);

细节:

void是关键字,表示此方法不返回任何关键值

方法通用定义格式
1
2
3
4
public static 返回值类型 方法名( 参数 ) {
方法体;
return 数据 ;
}
  • 定义方法时,要做到两个明确

    1. 明确参数:主要是明确参数的类型和数量
    2. 明确返回值类型:主要是明确方法操作完毕之后是否有结果数据,如果有,写对应的数据类型,如果没有,写void
  • 调用方法时

    void类型的方法,直接调用即可

    非void类型的方法,推荐用变量接受调用

方法常见问题
  • 方法不调用就不执行
  • 方法与方法之间是平级关系,不能嵌套定义
  • 方法的编写顺序和执行顺序无关
  • 方法的返回值类型为void,表示该方法没有返回值,没有返回值的方法可以省略return语句不写。如果要编写return,后面不能跟具体的数据
  • ruturn语句下面,不能编写代码,因为永远执行不到,属于无效的代码
方法重载(Overload)
方法重载关系的要求
  • 在同一个类中,定义了多个同名的方法,但每个方法具有不同的参数类型或参数个数,这些同名的方法,就构成了重载关系
判断方法

同一个类中,方法名相同,参数不同的方法

参数不同有三种:个数不同、类型不同、顺序不同

注意:识别方法之间是否是重载关系,只看方法名和参数,与返回值无关

好处
  • 不用记忆过多繁琐的方法名字

Day4

流程控制

流程控制语句
介绍

通过一些语句,来控制程序的执行流程

分类
  • 顺序结构

    Java默认的执行流程,没有特定的语法结构,按照代码的先后顺序,依次执行

  • 分支结构

    if、switch

  • 循环结构

    for、while、do…while

分支语句
if第一种格式
1
2
3
if (判断条件) {
语句体;
}

if 第一种形式执行流程如下:

  • 如果 条件表达式 为true,就执行下面的语句体
  • 如果 条件表达式 为false,就不执行

细节:IDEA的Debug调试执行到if语句的条件句时,可以选中括号中的判断条件,Debug会显示条件表达式的结果

if第二种格式
1
2
3
4
5
if (判断条件) {
语句体1;
} else {
语句体2;
}

if 第二种形式执行流程如下:

  • 如果 条件表达式 为true,就执行下面的语句体1
  • 如果 条件表达式 为false,就执行else下面的语句体2
if第三种格式
1
2
3
4
5
6
7
8
9
if (判断条件1) {
语句体1;
} else if (判断条件2) {
语句体2;
}
...
else {
语句体n+1;
}

if 第三种形式执行流程如下:

  • 如果 条件表达式1 为true,就执行下面的语句体1;
  • 如果 条件表达式1 为false,就继续判断条件表达式2;
  • 如果 条件表达式2 为true,就执行下面的语句体2;
  • 如果 条件表达式2 为false,就继续判断条件语句体3;
  • 如果 条件表达式3 为true,就执行下面的语句体3
  • 如果 条件表达式3 为false,就继续判断后面的表达式;
  • ….
  • 如果前面所有条件表达式判断都为false,就执行最后的else语句中的代码

细节:快捷键ctrl+shift+L整理代码

if语句注意事项
  • if语句中,如果大括号控制的是一条语句,大括号可以省略不写,但不建议
  • if语句的()和{}之间不要写分号
switch语句
  • switch语句格式和说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    switch(表达式) {			//表达式即为与值进行匹配的值
    case1:
    语句体1;
    break;
    case2:
    语句体2;
    break;
    ...
    default:
    /*如果没有匹配的值,则进行收尾,不是必要的*/
    语句体n+1;
    break;
    }

    switch分支的执行流程

    ① 先执行表达式的值,再拿着这个值去与case后的值进行匹配。

    ② 与哪个case后的值匹配为true就执行哪个case块的代码,遇到break就跳出switch分支。

    ③ 如果全部case后的值与之匹配都是false,则执行default块的代码。

switch语句注意事项
  • case后的值不允许重复

  • case后的值只能是字面量,不能是变量

  • switch()括号中的可接收类型为

    1. 基本数据类型:byte、short、char、int
    2. 引用数据类型:jdk5版本开始可以是枚举、jdk7版本开始可以是String字符串
  • case穿透现象

    switch语句中没有写break产生的现象,会执行匹配值的case中的语句体,并执行下方所有case的语句体,直到遇到break

    但如果使用得当,可以对代码进行优化,举例:

    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
    import java.util.Scanner;

    public class caseDemo {
    public static void main(String[] args) {
    System.out.println("请输入日期:");
    case1();
    }

    public static void case1() {
    Scanner sc = new Scanner(System.in);
    int date = sc.nextInt();
    switch (date) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    System.out.println("工作日");
    break;
    case 6:
    case 7:
    System.out.println("休息日");
    break;
    }
    }
    }
switch语句拓展
  • JDK14版本后,case后允许编写多个数据,用逗号分隔,举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.util.Scanner;

    public class caseDemo {
    public static void main(String[] args) {
    System.out.println("请输入日期:");
    case1();
    }

    public static void case1() {
    Scanner sc = new Scanner(System.in);
    int date = sc.nextInt();
    switch (date) {
    case 1,2,3,4,5:
    System.out.println("工作日");
    break;
    case 6,7:
    System.out.println("休息日");
    break;
    }
    }
    }
  • 更更更简便的方法——小箭头

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.util.Scanner;

    public class caseDemo {
    public static void main(String[] args) {
    System.out.println("请输入日期:");
    case1();
    }

    public static void case1() {
    Scanner sc = new Scanner(System.in);
    int date = sc.nextInt();
    switch (date) {
    case 1,2,3,4,5 -> System.out.println("工作日");
    case 6,7 -> System.out.println("休息日");
    }
    }
    }
if还是switch?

if:适用于范围性的判断

switch:适用于固定值的匹配

循环语句

介绍:将一段代码逻辑,重复执行多次

for语句
  • 格式

    1
    2
    3
    4
    5
    6
    7
    8
    for ( 初始化语句 ; 条件判断句 ; 条件控制语句 ) {
    循环体语句;
    }
    /*
    初始化语句用于定义循环的变量
    条件判断语句用于定义循环进行的条件
    条件控制语句用于改变初始化语句中的变量
    */
  • 执行流程

    1. 执行初始化语句,在整个循环过程中,只执行一次

    2. 执行判断条件,看其返回结果是true还是false

      false:循环结束

      true:执行第三步

    3. 执行循环体语句

    4. 执行条件控制语句

    5. 回到 2.

    注意:3,4的顺序影响循环条件的制定

  • 细节:

    循环中控制循环的那个变量,是可以在循环中继续使用的、

    循环的条件控制语句,不要局限的认为,只能是++

    循环中计数器变量的名称——count、

  • 注意事项

    1. for循环{}大括号中定义的变量,在每一轮循环结束后,都会从内存中释放
    2. for循环()小括号中定义的变量,在整个循环结束后,会从内存中释放
    3. for循环()与{}之间不要写分号
循环嵌套

嵌套循环从里到外看

细节:

  • System.out.println();ln的作用为每次打印数据后进行一次换行,去掉后就不会换行了

  • \t为输入制表符,使用格式:

    System.out.println( "\t" );

while循环语句
  • while循环格式与执行流程

    1
    2
    3
    4
    while (条件判断语句) {
    循环体语句;
    条件控制语句; //不要忘了这句
    }
    1. 执行初始化语句(在大括号外定义循环变量)

    2. 执行判断条件,看其返回结果为true或false

      false:循环结束

      true:进入第三步

    3. 执行循环体语句

    4. 执行条件控制语句

    5. 回到2.

do…while循环语句
  • do…while循环格式与执行流程

    1
    2
    3
    4
    do {
    循环体语句;
    条件控制语句;
    } while (条件判断语句);
    1. 执行初始化语句(同while)

    2. 执行循环体语句

    3. 执行条件控制语句

    4. 执行判断条件,看起返回结果是true还是false

      false:循环结束

      true:回到2.继续

  • 特点

    无论判断条件是否满足,都至少执行一次循环体(没什么用)

三种循环的区别
  • for和while先判断后执行,do…while先执行一次后判断

  • for循环(定义在小括号中的)用于控制循环的变量,在循环结束后,会从内存中消失。循环结束后,不能继续使用

    while循环用于控制循环的变量,在循环结束后,变量还可以继续使用

    两个循环没什么区别,可以互相用

跳转控制语句
  • 基本语句

    break:结束循环和switch语句,也只能在这两个中使用

    continue:跳过本次循环,继续下一次循环,只能在循环中使用

    注意:两者同级的下一句不能有代码

  • 三种死循环格式

    1. for(;;) {}
    2. while(true) {}
    3. do {} while (true);
  • break的标号功能

    当存在嵌套循环,或者循环内部有switch语句并且想要从内部跳出某一层循环时,可以使用标号功能,如例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.util.Scanner;

    public class breakBiaoHao {
    public static void main(String[] args) {
    BreakBiaoHao();
    }
    public static void BreakBiaoHao() {
    lo: //标号
    for(;;) {
    System.out.println("请输入数字,1为继续死循环,2为跳出死循环");
    Scanner sc = new Scanner(System.in);
    int choice = sc.nextInt();
    switch (choice) {
    case 1:
    System.out.println("死循环继续");
    break;
    case 2:
    System.out.println("跳出死循环");
    break lo; //使用标号
    }
    }
    }
    }
Random随机数
  • Random使用步骤

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 目标:掌握使用Random生成随机数的步骤。
    // 1、导包。import java.util.Random; (idea会自动完成)
    import java.util.Random;
    public class RandomDemo1 {
    public static void main(String[] args) {
    // 2、创建一个Random对象,用于生成随机数。
    Random r = new Random();
    // 3、调用Random提供的功能:nextInt得到随机数。
    for (int i = 1; i <= 20; i++) {
    int data = r.nextInt(10); // 0 - 9
    System.out.println(data);
    }
    }
    }

    如果随机数想要以1开头的随机数的话,需要自行进行加1操作,如下

    int data = r.nextInt(10) + 1;范围为1~10

细节:死循环如果没有出口的话IDEA会报错

Day5

数组(array)

数组就是一个容器,用来存一批同种数据类型的多个值的。

数组静态初始化

初始化:在内存中,为数组容器开辟空间,并将数据存入容器中的过程

数组定义类型
  • 格式一

    数据类型[] 数组名;

    例如:int[] array;

  • 格式二

    数据类型 数组名[];

    例如:int array[];

细节:JAVA中多用第一种格式

注意:这种定义格式,定义出来的只是数组类型的变量,内存中还没有创建出数组容器

数组静态初始化格式
  • 完整格式

    数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};

  • 简化格式

    数据类型[] 数组名 = {元素1,元素2,元素3...};

    例如:int[] arr1 = {11,22,33};

拓展——直接输出数组名
1
2
3
4
5
6
7
8
9
10
11
public class shuZuDemo {
public static void main(String[] args) {
arrayTest1();
}
public static void arrayTest1() {
int[] arr1 = {11,22,33};
double[] arr2 = {11.1,22.2,33.3};
System.out.println(arr1); //直接打印变量名
System.out.println(arr2);
}
}

输出为

1
2
[I@b4c966a
[D@2f4d3709

@:分隔符

[:表示当前空间为数组类型的空间

D:表示当前空间的数组类型

b4c966a:数组的十六进制内存地址

数组元素访问
数组元素访问格式
  • 格式:数组名[索引]

索引:索引是数组容器中空间对应的编号,编号从0开始,逐个+1

数组遍历操作
数组遍历介绍
  • 数组遍历:将数组中所有的元素,取出来进行操作
动态获取数组长度

数组名.length是一个用于获取数组长度的属性,可以用于打印、判断等操作

细节:属性是类中用于存储数据的变量,它用于描述类或对象的状态,上面描述数组长度的属性,它的值在数组创建后不能被修改。

数组遍历快捷键

数组名.fori+回车可以直接搭建如下代码

1
2
3
for (int i = 0; i < arr.length; i++) {

}
拓展——sout快捷键

在IDEA中输入元素.sout,并回车,可以直接对元素进行包裹

拓展——批量修改变量名快捷键

IDEA中选中变量名,shift+F6,即可批量修改变量名

数组动态初始化

动态初始化:初始化时只指定数组长度,系统会分配默认值

格式:数据类型[] 数组名 = new 数据类型[长度]

默认值的分类:

  • 整数:0

  • 小数:0.0

  • 布尔:false

  • 字符:’\u0000’

    以\u开头+四个数字的字符为Unicode字符,\u0000常见为空白字符

  • 引用数据类型:null

    引用数据类型:数组、类、接口…

    字符串String属于一种类

    故可说字符串类型数组的默认值为null

数组长度也可为变量

两种初始化的区别

数组长度是否手动指定,数组元素是否手动指定

拓展——抽取方法

选中需要抽取的代码,ctrl+alt+m,然后可以修改方法的名字

数组内存图
Java内存分配介绍
  • 具体见前文——方法调用内存图解

  • new出来的东西会在堆内存中开辟空间并产生地址

  • 方法区

    具体见前文——方法调用内存图解

  • 本地方法栈

    辅助虚拟机工作,了解即可

  • 寄存器

    cpu中的内存,了解即可

过程演示:

代码:

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
int[] arr = {11,22,33};

arr[1] = 44;
arr[2] = 55;
arr[3] = 66;

System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
}

过程:

  1. 字节码文件进入方法区,main方法处于候命状态

  2. 主方法被虚拟机自动调用执行,main方法进入栈内存

  3. main方法在栈内存中逐个代码运行,首句int[] arr = {11,22,33};运行

    此句代码为定义数组的简化版,实际代码为int[] arr = new int[]{11,22,33};,代码中存在new

  4. 堆内存中开辟一块整数类型的数组空间,数组长度为3,则把数组空间分为三块,分别储存11,22,33

    开辟空间的同时,堆内存中数组空间的地址值被返回栈内存中的main方法,main方法可按照地址值找到数组空间并对其进行更改

  5. main方法的改变变量值语句与打印语句根据地址值对数组空间的值进行操作

  6. 语句执行完成,main方法弹栈,程序结束

两个数组指向相同内存图

如图

第四行代码中,数组array1已经在堆内存中开辟了一块空间,如笔记前文可知,如果直接输出array1这个变量名后,命令行中会直接输出数组的地址值,故第六行代码中是将数组array1的地址也交给了数组array2,两个数组同时管理堆内存中的一块数组空间,故用array2更改堆内存的数值后,array1的数值也进行了改变

面试题——方法的参数传递
  1. 基本数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class ArgsTest1 {
    public static void main(String[] args) {
    int number = 100;
    System.out.println("调用change方法前:" + number);
    change(number);
    System.out.println("调用change方法后:" + number);
    }

    public static void change(int number) {
    number = 200;
    }
    }

    两次输出的number变量分别为什么?答案与解析见12:20

    https://www.bilibili.com/video/BV1Fv4y1q7ZH?spm_id_from=333.788.player.switch&vd_source=a67a69fa2d51203b8c6f8ebbc5bfe21a&p=55

  2. 引用数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class ArgsTest2 {
    public static void main(String[] args) {
    int[] arr = {11, 22, 33, 44, 55};
    System.out.println("调用change方法之前:" + arr[0]);
    change(arr);
    System.out.println("调用change方法之后:" + arr[0]);
    }

    public static void change(int[] arr) {
    arr[0] = 66;
    }
    }

    两次输出的数组arr[0]分别是什么?答案与解析见21:15

    https://www.bilibili.com/video/BV1Fv4y1q7ZH?spm_id_from=333.788.player.switch&vd_source=a67a69fa2d51203b8c6f8ebbc5bfe21a&p=55

总结:

方法参数

如果传递的是基本数据类型,则传递的参数是数据值

如果是引用数据类型,则传递的参数是地址值

提问:Java到底是值传递还是址传递?

JAVA之父回答:地址值也是值

数组常见问题
数组索引越界异常(ArrayIndexOutOfBoundsException)
空指针异常(NullPointerException)

当引用数据类型的变量,被赋值为null后,地址的指向被切断

如果此时还想去访问原内存中的数据,就会出现空指针异常

如下

1
2
3
4
5
6
7
public static void main(String[] args) {
int[] arr = new int[2];
// 把null赋值给数组
arr = null;
// 输出元素
System.out.println(arr[0]);
}

二维数组

二维数组介绍
  • 二维数组是一种容器,该容器用于储存一维数组
二维数组静态初始化
格式
  • 完整格式

    数据类型[][] 数组名 = new 数据类型[][] {{元素1,元素2},{元素1,元素2}};

    例子

    int[][] arr = new int[][]{{11,22},{33,44}};

  • 简化格式

    数据类型[][] 数组名 = {{元素1,元素2},{元素1,元素2}};

    例子

    int[][] arr = {{11,22},{33,44}};

  • 常用格式

    1
    2
    3
    4
    int[][] arr = {
    {11,22,33},
    {44,55,66}
    };
二维数组访问格式

arr[索引m][索引n]

索引m:指定访问哪一个一维数组

索引n:访问一维数组中的哪个元素

拓展——直接输出二维数组数组名

如例

1
2
3
4
5
6
7
8
9
10
public class test1 {
public static void main(String[] args) {
int[][] arr = {
{11,22,33},
{44,55,66}
};
System.out.println(arr);
}
}

结果为[[I@b4c966a,比一维数组多了一个[

以一维数组索引的形式输出如例

1
2
3
4
5
6
7
8
9
public class test1 {
public static void main(String[] args) {
int[][] arr = {
{11,22,33},
{44,55,66}
};
System.out.println(arr[0]);
}
}

结果为[I@b4c966a,可见二维数组存储一维数组时,存储的是一维数组的地址,而不是一维数组整体

拓展——重命名快捷键

shift+F6

二维数组遍历
  1. 遍历二维数组,获取到每一个一维数组
  2. 继续遍历一维数组,获取具体的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class test1 {
public static void main(String[] args) {
int[][] arr = {
{11,22,33},
{44,55,66}
};
printArray2(arr);
}
public static void printArray2(int[][] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
//arr[i]为二维数组储存的每一个一维数组
System.out.println(arr[i][j]);
}
}
}
}
二维数组动态初始化
格式

数据类型[][] 数组名 = new 数据类型[m][n]

m:二维数组中可以存放多少个一维数组

n:每一个一维数组中可以存放多少个元素

也可以将提前创建好的一维数组存储到二维数组中,这个过程通过对二维数组的地址值进行更改以达成

Day6

Day7

面向对象编程(Object Oriented Programming)(OOP)

  • 学习如何设计对象
  • 学习已有的对象如何使用
类和对象
类的介绍
  • Java中想要创建对象,必须先要有类的存在
  • 类指的是一组相同属性和行为的集合,我们将其理解为是一张对象的设计图
类和对象的关系
  1. 依赖关系:Java中需要根据类,创建对象
  2. 数量关系:一个类,可以创建出多个对象
类的组成

类的本质:对事物的描述

  • 属性(对事物用名词描述)

    用成员变量表示,跟之前定义变量的格式一样,只不过位置需要放在方法的外面

  • 行为(对事物用动词描述)

    用成员方法表示,跟之前定义方法的格式一样,只不过需要去掉static关键字

例子:

有一个学生,姓名为Chihaya Anon,生日是9月8日,身高为160,会弹技术一般的吉他和起品味爆炸级别的名字

则描述这个学生类可用以下属性和行为描述:

属性:姓名、生日、身高

行为:弹技术一般的吉他、起品味爆炸级别的名字

类的创建

则可用以下代码创建这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.itheima.oop;

public class Student {
//属性:姓名、生日、身高
String name = "Chihaya Anon";
String birthday = "9月8日";
int height = 160;
//行为:弹技术一般的吉他、起品味爆炸级别的名字
public void guitar() {
System.out.println("她会弹技术一般的吉他");
}
public void setName() {
System.out.println("她会起品味爆炸级别的名字");
}
}
类的使用

类的使用需要在与对象同个软件包的测试类(含有主方法的类)中

(无主方法的类无法执行,程序无接口)

  1. 创建对象的格式

    类名 对象名 = new 类名();

  2. 使用对象成员变量的格式

    对象名.成员变量;

  3. 使用对象成员方法的格式

    对象名.成员方法();

如例:用输出使用对象成员的变量与方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.itheima.oop;

public class StudentTest {
public static void main(String[] args) {
//用Student类创建名为stu1的对象
Student stu1 = new Student();
//输出对象成员变量
System.out.println(stu1.name);
System.out.println(stu1.birthday);
System.out.println(stu1.height);
//使用对象成员方法
stu1.guitar();
stu1.setName();
}
}

输出为

1
2
3
4
5
Chihaya Anon
9月8日
160
她会弹技术一般的吉他
她会起品味爆炸级别的名字
使用细节
  1. 打印对象名,可以看到对象的内存地址

    如例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.itheima.oop;

    public class StudentTest {
    public static void main(String[] args) {
    //用Student类创建名为stu1的对象
    Student stu1 = new Student();
    //打印对象名
    System.out.println(stu1);
    // //输出对象成员变量
    // System.out.println(stu1.name);
    // System.out.println(stu1.birthday);
    // System.out.println(stu1.height);
    // //使用对象成员方法
    // stu1.guitar();
    // stu1.setName();
    }
    }

    输出为

    com.itheima.oop.Student@2f4d3709

    com.itheima.oop.Student为全类名,由包名(com.itheima.oop.)和类名(Student)组成

    @为分隔符

    2f4d3709为对象stu1在堆内存中的地址

  2. 成员对象没有赋值也可直接使用,使用的是对象的默认值

    类是描述这一类事物的设计图,而不应该只描述这一类事物中的某个个体,故编写类的时候不应对类赋值,成员方法同,如例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.itheima.oop;

    public class Student {
    //属性:姓名、生日、身高
    String name;
    String birthday;
    int height;
    //行为:爱好1、爱好2
    public void hobby1(String hobby1) {
    System.out.println(hobby1);
    }
    public void hobby2(String hobby2) {
    System.out.println(hobby2);
    }
    }
    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
    package com.itheima.oop;

    public class StudentTest {
    public static void main(String[] args) {
    //用Student类分别创建名为stu1和stu2的对象
    Student stu1 = new Student();
    Student stu2 = new Student();
    //对对象stu1的各个属性赋值
    stu1.name = "Chihaya Anon";
    stu1.birthday = "9月8日";
    stu1.height = 160;
    //对对象stu2的各个属性赋值
    stu2.name = "Takamatsu Tomori";
    stu2.birthday = "11月22日";
    stu2.height = 155;
    //打印对象stu1和stu2的属性
    System.out.println(stu1.name);
    System.out.println(stu1.birthday);
    System.out.println(stu1.height);
    //调用对象stu1的两个行为
    stu1.hobby1("她会弹技术一般的吉他");
    stu1.hobby2("她会起品味爆炸级别的名字");
    System.out.println("-------------------------------");
    System.out.println(stu2.name);
    System.out.println(stu2.birthday);
    System.out.println(stu2.height);
    //调用对象stu2的两个行为
    stu2.hobby1("她会捡地上的石头吃");
    stu2.hobby2("她会唱春日影");
    }
    }

    结果为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Chihaya Anon
    9月8日
    160
    她会弹技术一般的吉他
    她会起品味爆炸级别的名字
    -------------------------------
    Takamatsu Tomori
    11月22日
    155
    她会捡地上的石头吃
    她会唱春日影
对象内存图
单个对象内存图

以以下代码为例子:

1
2
3
4
5
6
7
8
9
10
11
12
public class Student {
String name;
int age;

public void study() {
System.out.println("学习Java");
}

public void eat() {
System.out.println("吃饭");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestStudent {
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu);
System.out.println(stu.name);
System.out.println(stu.age); // 注意:代码中有拼写错误,应为 stu.age 而不是 stu.sge
stu.name = "张三";
stu.age = 23; // 注意:代码中有拼写错误,应为 stu.age 而不是 stu.sge
System.out.println(stu.name);
System.out.println(stu.age); // 注意:代码中有拼写错误,应为 stu.age 而不是 stu.sge
stu.study();
stu.eat();
}
}
  1. 方法区

    带有主方法的类是程序入口,故StudentTest类字节码文件先进入方法区

  2. 栈内存

    主方法进栈,执行第三行代码,引用了Student类

  3. 方法区

    主方法引用了Student类,故Student类字节码文件进入方法区

  4. 栈内存

    定义一个以Student为数据类型的变量stu

    Student是一个类,类属于引用数据类型

  5. 堆内存

    堆内存中根据Student类生成对象stu,具有Student类中创建的成员变量,且为默认数值;成员方法不会直接进入堆内存开辟的对象空间中,而是用一块空间记录Student类中成员方法的引用地址,以便根据地址值使用成员方法

  6. 栈内存

    对象stu接收堆内存中创建的对象空间的地址

    继续执行主方法中的代码

    第11、12行代码使用方法study和eat,栈内存根据对象stu的地址访问堆内存中的对象stu空间,再根据堆内存中的stu对象空间中的成员方法引用地址找到方法区中的Student类字节码文件中的成员方法,并调用

    类Student中的study方法进栈,执行代码,弹栈,eat方法同上,实现成员方法的调用

两个对象内存图(方法共用内存图)

主要为了加深印象,可以看原视频https://www.bilibili.com/video/BV1Fv4y1q7ZH?spm_id_from=333.788.player.switch&vd_source=a67a69fa2d51203b8c6f8ebbc5bfe21a&p=71

9:11

注意:

多个对象在堆内存中有各自的对象空间,互不干扰

多个对象用的是同一字节码文件中的方法

两个引用指向相同内存图

与数组内存同理

成员变量和局部变量
成员变量和局部变量的区别

对象的消失,是指记录对象在堆内存中地址的方法弹栈

细节:当记录对象地址的方法弹栈后,堆内存中的对象内存变为垃圾内存,Java中的垃圾回收器不定时自动清理,而C语言的垃圾内存只能手动清理

this关键字
this可以解决的问题

成员变量和局部变量重名,Java使用就近原则,谁后被赋值,谁被调用

若想随意使用成员变量,则可用this关键字进行区分

构造方法
封装