Skip to content

Latest commit

 

History

History
682 lines (469 loc) · 33 KB

File metadata and controls

682 lines (469 loc) · 33 KB

JavaReview01_Java基础语法:

@author Thuiww

@date 2025-01-22


一、预备知识Review:

1.1 什么是 JDK,JRE?

  • JDK (Java Development Kit):Java程序开发工具包
  • JRE (Java Runtime Environment):Java程序运行环境

1.2 各进制如何表示?

***<补充>:***这里的各进制的表示,指的是在编程中如何表示当前数字是多少进制

  • 二进制:
    由0,1组成。以 0b 开头*(注意是 数字0 而不是字母o)*
    如:0b1100b1101100b112110
  • 八进制:
    由0,1,2,3,4,5,6,7组成。以 0 开头*(注意是 数字0 而不是字母o)*
    如:0371026510128
  • 十六进制:
    由0,1,......9,a,b,c,d,e,f(大小写均可)组成。以 0x 开头*(注意是 数字0 而不是字母o)*
    如:0x5A290XABC0x89AF
  • 十进制:
    由0,1......,9组成。默认整数。

1.3 什么是字符集与编码?

字符集:

字符集是一组字符的集合,通常包括字母、数字、符号和特殊字符等,用于表示书写系统中的所有可能字符。每一个字符都有相对应的字符集代码点

  • 例子
    • Unicode 字符集:一个全球通用的字符集,包含几乎所有书写系统的字符。
    • GBK 字符集:一个针对简体中文的字符集,包含中文汉字、繁体汉字等,一些常用符号。

编码:

编码是将字符集中的字符映射为二进制数据的规则或方法,用于存储或传输字符。因为字符集只是单纯字符和字符集代码点*(码点)*的一一对应,其中并不包含这些代码点在计算机中如何存储,以及计算机该如何依次识别这些代码点。所以,编码由此诞生,编码是以相对应的字符集为基础,在字符集码点的基础上重新再定义编码的码点,然后定义这些编码码点占用几个字节(以便于计算机识别),即:计算机识别到这些编码码点时,该按照几个字节来依次读取

  • 例子
    • UTF-8、UTF-16、UTF-32编码 是 Unicode 字符集的编码方式。
    • GBK 编码 是 GBK 字符集的编码方式。

1.4 Unicode字符集:

1.4.1什么是Unicode字符集:

  • Unicode字符集是一个用于表示世界上几乎所有书写系统的字符标准,支持 100 多种语言和符号,它的目标是为每个字符分配一个唯一的代码点,以实现跨平台、跨语言的统一编码和互操作性。
  • Unicode代码点书面表示: **U+**是一种通用的表示方法(其后面跟的码点要以十六进制表示),用于描述Unicode 字符集中的码点。它仅仅是逻辑上的一种约定,不直接与存储或程序中的具体编码格式绑定。如:应用于文档、书籍、技术规范中表示Unicode码点。 如:U+0041 表示字符 AU+6C49 表示汉字 U+1F60A 表示表情符号 😊。
  • 编码空间(Code Space): Unicode 的编码空间范围是从 U+0000U+10FFFF,理论上支持 111 万多个字符。(21位:1 0000 1111 1111 1111 1111
  • 未分配区域:在 Unicode编码空间中(u+0000~u+10FFFF),也含有未分配码点区域,如:U+D800U+DBFFU+DC00U+DFFF,这两个区域分别作为 utf-16编码的低位代理和高位代理,均不映射任何字符;除此之外其中还有很多未分配区域,专门用于表示其他的一些含义
  • 常见字符:一些常见的字符和语言(英语、汉语、韩语、拉丁语,等)均在前16位之前(u+FFFF之前)完成相应的映射,而对于 u+10000~u+10FFFF所映射的字符,均为语言中一些不常用字符,其中也包含一些表情符号,如:𪛖U+2A6D6,😊U+1F60A

1.4.2 Unicode字符集对应三种编码:

  • utf-8:

    • 概述:变长编码*(1~4 个字节表示)*,每个字节的高位用于标识字节的数量和字符的范围,使得计算机可以依次识别。为了兼容ASCII,其中,ASCII(U+0000U+007F)用一个字节表示,两个字节一般表示拉丁文等等,三个字节一般表示中文、韩文、日文等等,四个字节一般表示各种生僻字、特殊符合和表情等等。
    • 编码规则
      • 1 字节:字节的最高位为 0,二进制表示:0xxx xxxx
        后面依次对应:码点 U+0000U+007F的字符,占据7位
      • 2 字节:字节的最高 3 位为 110,第二字节的最高 2 位为 10。二进制表示:110x xxxx 10xx xxxx
        后面依次对应:码点 U+0080U+07FF 的字符,占据11位
      • 3 字节:字节的最高 4 位为 1110,后两字节的最高 2 位为 10。二进制表示:1110 xxxx 10xx xxxx 10xx xxxx
        后面依次对应:码点 U+0800U+FFFF的字符,占据16位
      • 4 字节:字节的最高 5 位为 11110,后面三个字节的最高 2 位为 10。二进制表示:1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
        后面依次对应:码点 U+10000U+10FFFF的字符,占据21位
    • 示例:
      • A:Unicode 码点 U+0041(二进制(属于1 字节->7位):100 0001):
        UTF-8 编码41(1 字节 8位:0100 0001)
      • :Unicode 码点 U+6C49(二进制(属于3 字节->16位):0110 1100 0100 1001):
        UTF-8 编码E6 B1 89(3字节 24位:1110 0110 1011 0001 1000 1001)
      • 😊:Unicode 码点 U+1F60A(二进制(属于4 字节->21位):0 0001 1111 0110 0000 1010):
        UTF-8 编码F0 9F 98 8A(4字节 32位:1111 0000 1001 1111 1001 1000 1000 1010)
  • utf-16:

    • 概述:变长编码*(2 或 4 个字节表示)*

      • 对于 BMP 字符(基本多文种平面)(Unicode:U+0000U+FFFF),UTF-16 使用 2 字节表示,与 Unicode字符集一一映射,两字节中前面空余位数补零
      • 对于 增补字符U+10000U+10FFFF),UTF-16 使用 **代理对(2 个 16 位单元)**来表示,即使用 4 个字节。即,最终得出的utf-16编码会落于:高位代理:U+D800U+DBFF, 低位代理:U+DC00U+DFFF,这两个区间段,高位加低位共32位(四字节)组成一个utf-16编码,计算机通过识别第一个字符如果落于U+D800U+DBFF则说明这是增补字符,将一次性读取四个字节,继而判断后两个字节是否落于U+DC00U+DFFF字段,如果是则显示增补字段,若不是则报错编码异常。如果一开扫描两个字节不处于U+D800U+DBFF,则说明是BMP字符,即正常显示这两个字节映射的字符即可。
    • 编码规则

      • 2 字节:将 Unicode字符集码点前16位范围内的字符直接一一映射于两字节中,前面空余位数补零
        对应Unicode码点范围:u+0000u+FFFF

      • 4 字节:将 Unicode字符集码点16位到21位范围内的字符,经拆分并进行相应运算,分为高16位和低16位,将高、低一一映射到四个字节中
        对应Unicode码点范围:u+FFFFu+10FFFF
        <补充>:经过拆分和运算得到的高位范围必在:U+DC00U+DFFF 之间,低位范围必在:U+DC00U+DFFF之间

    • 示例:

      • A:Unicode 码点 U+0041
        UTF-16 编码0041(2字节)
      • :Unicode 码点 U+6C49
        UTF-16 编码6C49(2字节)
      • 😊:Unicode 码点 U+1F60A
        UTF-16 编码D83D DE0A(4字节)
  • utf-32:

    • 概述:定长编码*(4 个字节表示)*,将 Unicode字符集码点范围内的字符(0~21位)直接一一映射于四字节中,前面空余位数补零。无论字符的 Unicode 码点范围如何,UTF-32 总是使用 4 个字节来表示字符。

    • 编码规则

      • 4 字节:直接使用 4 字节存储 Unicode 码点的二进制表示。空位补零
    • 示例:

      • A:Unicode 码点 U+0041
        UTF-32 编码00000041(4 字节)
      • :Unicode 码点 U+6C49
        UTF-32 编码00006C49(4 字节)
      • 😊:Unicode 码点 U+1F60A
        UTF-32 编码0001F60A(4 字节)

对比总结:

字符 Unicode 码点 UTF-8 UTF-16 UTF-32
A U+0041 41 (1 字节) 0041 (2 字节) 00000041 (4 字节)
U+6C49 E6 B1 89 (3 字节) 6C49 (2 字节) 00006C49 (4 字节)
😊 U+1F60A F0 9F 98 8A (4 字节) D83D DE0A (4 字节) 0001F60A (4 字节)

1.4.3 代码中通过 utf-8/16/32 码点映射字符:

使用转义字符:\u

  • \u 是一种在编程语言中使用的转义序列,用于在源代码中表示 基于Unicode 字符集编码的码点(utf-8/16/32)。
  • 适用场景:Java、JavaScript、C#、Python 等编程语言,用于在 字符串字符中插入 Unicode 字符。

<注意>:使用 \u 转义字符时,必须了解编程语言的内部字符编码方式,以及它如何解析 \u 后的字符。这是因为不同的语言和其所使用的编码方式会直接影响转义字符的解析和存储。如:

  • Java内部字符编码采用 utf-16编码,当需要通过转义字符 \u来表示某一个属于unicode字符集中字符时,必须满足 utf-16代码点,如:😊UTF-16 编码:D83D DE0A(四个字节),即\uD83D\uDE0A
  • **C/C++**其内部字符编码基于实现,若是基于utf-32实现,那么使用转义字符输出😊表情,可以使用\U(后接8位16进制),UTF-32 编码:0001F600(四个字节),即\U0001F600

字符规则

  • \u 转义字符的使用仅限于基于 Unicode 字符集的编码
  • \u 后面必须紧跟 4 位 16 进制数字,如:
    • \u0041 表示字符 A
    • \u6C49 表示汉字
  • 如果需要表示超出 4 位的 Unicode 码点(例如 U+1F60A),需要使用特殊的表示方式:
    • 在 Java(utf-16)
      • 使用 代理对(Surrogate Pair):"\uD83D\uDE0A"
    • 在 C/C++(utf-8/16/32)
      • 使用扩展形式:"\U0001F60A"

示例:

//Java示例(utf-16)
System.out.println('\u0041'); // 输出: A
System.out.println("\u6C49"); // 输出: 汉
System.out.println("\uD83D\uDE0A"); // 输出: 😊
# Python 示例(utf-8/16/32)
print('\u0041')        # 输出: A
print('\u6C49')        # 输出: 汉
print('\U0001F60A')    # 输出: 😊

对比 u+\u:

属性 U+ \u
使用场景 描述 Unicode 码点(逻辑概念) 编程语言中的 基于Unicode编码的 序列
表示内容 一个逻辑码点,用于标识 Unicode 字符 用于在代码中嵌入字符(需转义)
语法要求 U+ 后接码点(十六进制数字) \u 后必须跟 4 位十六进制数字
扩展字符表示 不需要额外处理,直接表示 需要代理对或扩展语法来支持 4 字节字符

1.5 其他字符集:

1.5.1 GBK字符集:

GBK字符集:

  • GBK字符集: 是中国国家标准编码,它既可以看作一种字符集,也可以看作一种编码方案,但这取决于具体语境。GBK 是对 GB2312(1980年发布)的扩展。其扩展了繁体字、少数民族文字或更多国际字符*(常用的日文、韩文、拉丁文等)*,总计字符数约 21,886 个字符(15位)
    • GB2312部分:收录了6,763个字符(包括汉字和其他符号)。
    • 扩展部分:收录了15,123个字符,主要包括汉字、日文假名、韩文字符和其他特殊符号。
  • GBK编码:变长编码*(1 或 2 个字节表示)*,兼容ASCII且占据一个字节,当计算机识别首字节第一位为 0 时,则默认将第一个字节作为单独一个字符来识别,从而识别ASCII字符,当首字节第一位不为 0 时,继而再继续识别第二个字节,将第一个字节和第二字节一并识别为一个字符,从而识别除ASCII字符以外的其他字符(如:中文,等)
    • 1 字节:对应 ASCII字符集 的内容(00 ~ 7F),与ASCII完全兼容
    • 2 字节:用于表示中文汉字和扩展字符。范围是81至 FE(第一个字节)和 40 至 FE(第二个字节)
  • 示例:
    • 单字节字符:41(对应字符 'A'
    • 双字节字符:B0A1(对应中文 '啊'

1.5.2 Latin-1(ISO 8859-1)字符集:

Latin-1字符集:

  • Latin-1字符集: 是欧洲常用的一种字符集,又称为 Latin Alphabet No.1,主要覆盖西欧语言的字符。于1987年发布,广泛用于早期计算机系统中。每个字符占用1个字节,便于处理和存储。仅支持西欧语言,无法表示东欧、中文、日文等复杂语言字符。
    • 支持语言:拉丁语系语言(如英语、法语、德语、西班牙语、葡萄牙语、意大利语等)。
    • 字符总数256个字符(8位),其中前128个字符与ASCII完全一致,后128个字符扩展了西欧语言中的特殊符号和字母(如带重音符号的字母:é、ä、ø等)。
    • 后续扩展: 为了支持更多语言,ISO 8859 系列还推出了其他版本,如ISO 8859-2(东欧语言)、ISO 8859-3(南欧语言)等。

Latin-1编码: 定长编码(1个字节),字节范围为 00 ~ FF

  • 前128个字符(00 ~ 7F):与ASCII字符集完全一致,表示标准的英文字母、数字和常用符号。
  • 后128个字符(80 ~ FF):扩展字符,用于表示西欧语言的特殊字符(如é、ç、ñ等)以及控制字符。

示例:

  • 单字节字符:
    • 0x41(ASCII 'A')
    • 0xC9(拉丁字母 'É')
  • 不可用的字符: 超出范围的字符(如中文‘你’)无法在Latin-1中表示。

1.5.3 <补充> 什么是ANSI编码 ?

ANSI编码:

  • ANSI编码并不是单一的字符集或编码标准,而是一个广义术语,用于表示微软在Windows系统中使用的本地化编码方案。也就是说ANSI编码是一个代指编码,指的是当前Windows使用的某种编码,统一代称为ANSI编码。其根据当前Windows使用的时区地区的不同从而使用不同的字符集编码(ISO 8859-1、GBK、Shift-JIS, 等等)

  • ANSI编码的代码页: ANSI编码采用代码页(Code Page)的方式,根据时区地区从而选择不同的字符集。例如:

    • 代码页 1252(Western European): 西欧语言(基于ISO 8859-1)(美国、英国等国家也是用)。
    • 代码页 936(GBK): 用于简体中文,兼容GB2312和部分GBK扩展字符。
    • 代码页 932(Shift-JIS): 用于日语。
    • 代码页 1251(Cyrillic): 用于俄语等斯拉夫语言。

    所以,ANSI是一种泛指的编码方案,其编码类型依赖于代码页,代码页原则依据与当前Windows时区设置,可以通过改变Windows时区从而决定ANSI编码的选择。

  • Windows区域设置与ANSI编码的关系:

    区域 语言 ANSI代码页 编码特点
    中国(简体中文) 中文(简体) 936 GBK(变长编码,1或2字节)
    日本 日文 932 Shift-JIS(变长编码,1或2字节)
    美国 英语(美国) 1252 Latin-1(单字节编码)
    俄罗斯 俄语 1251 Cyrillic(单字节编码)

软件如何处理ANSI编码:

  • 软件层面:(不正当的使用会产生乱码)

    • 如果软件明确支持Unicode(如UTF-8),则不会受到ANSI默认编码的影响,能够正确处理国际化文本(由自身解码)。
    • 如果软件只支持ANSI,那么它会默认按当前系统的代码页来解码内容。
      如:如果将时区设置成美国(代码页 1252:ISO 8859-1),则当前系统中如果有部分软件只支持ANSI(如:VC++6.0)则,这些软件系统本身中的中文字符就会乱码。如:软件系统中的中文字符:菜单(GBK编码:B2CB B5A5)会被解码成²Ëµ¥(ISO依次解码B2 CB B5 A5);
      • B2:在代码页 1252 中对应的字符是 ²(上标 2)。
      • CB:对应的字符是 Ë(拉丁字母 E 带两个点)。
      • B5:对应的字符是 µ(希腊字母 μ 或表示微米)。
      • A5:对应的字符是 ¥(日元符号)。

    当下大部分软件都支持Unicode自身解码,通常自带解码器,自行处理文本解码,而不是依赖操作系统的默认ANSI设置,能正确处理跨语言、跨区域的文本。但是对于部分老旧软件就不太支持自身解码采用默认 ANSI解码。
    如:

    • CMD(命令提示符):默认使用ANSI(代码页)解码,受系统区域设置影响。
    • VC++6.0:默认ANSI解码,受系统区域设置影响
    • PowerShell:支持UTF-8等多种编码格式,支持更好的跨语言兼容。

二、Java基础:

2.1 名词解析:

基础名词解析: 关键字、标识符、变量

  • 关键字:Java语言赋予了特殊含义,用作专门用途的字符串
    • 示例:class, public, static, void, int , long, ... ...等等已经被 Java定义好的单词
  • 标识符:Java中变量名、方法名、类名、包名、常量名等,官方命名或自身命名使用的字符序列
    • 规范&示例:
      • 包名:所有单词所有字母均小写:aaabbbccc
        例如:java.long、com.tang.bean
      • 类名、接口名:所有单词首字母大写:AaaBbbCcc
        例如:String、System、HelloWorld
      • 变量名、方法名:第一个单词首字母小写,其余单词首字母大写:aaaBbbCcc
        例如:name、age、studentCategory、method()、getName()
      • 常量名:所有单词所有字母均大写,单词间下划线分割:AAA_BBB_CCC
        例如:MAX_SIZE、DEFAULT_VALUE
  • 变量:
    • 构成:数据类型、变量名、变量值
    • 格式:数据类型 变量名 = 变量值

2.2 数据类型:

Java中数据类型分为两大类:

  • 基本数据类型(8个):**整形:**byte, short, int, long, **浮点型:**float, double, **字符型:**char, **布尔型:**boolean
  • 引用数据类型:数组 []、类 class、接口 interface、枚举 enum、注解 @interface、记录 record

整形:

  • Java中 整形常量 默认为 **int **型,即在各个变量之间运算 JVM默认以 int型来对整形进行判断。(对于整形常量的赋值,并不受影响,只要不超过其整形数据类型范围即可)

    byte b1 = 127;	//ok
    byte b2 = 128;	//compile error:超出byte范围
    
    byte b3 = 1;
    byte b4 = 2;
    byte b5 = b3 + b4;	//compile error:损失精度:不能将int型赋给byte型
    byte b6 = 1 + b4;	//compile error:损失精度:不能将int型赋给byte型
    byte b7 = b3 + 2;	//compile error:损失精度:不能将int型赋给byte型
    byte b8 = 1 + 127;	//compile error:超出byte范围
    byte b9 = 1 + 2;	//ok:变量之间,或变量与常数之间相加减,因为JVM不能判断其是否超出默认范围,所以统一使用 int型来判断
    
    long l1 = 12345678912;	//compile error: 超出int范围
    long l2 = 12345678912L;	//ok: 给数据类型long赋值时,因为JVM默认判断的是int型,所以当变量值超过int范围就会报错整数过大,这时候若要存储long型变量值,需要在变量值后面加 L ,表示这是一个long型变量值

浮点型:

  • Java中浮点型常量默认为double型,且因为double是双精度比float单精度的精度要高,并且不同于整形常量直接赋值,一个浮点型常量的直接赋值,系统不会根据当前浮点型类型进行判断,若没有特殊说明统一默认为double类型

    float f1 = 1.0;	//compile error:损失精度:不能将double型赋给float型
    float f2 = 1.0f;	//ok: 对于浮点型变量值要想赋值给float变量,必须添加f声明其为浮点型变量值
    double d = 1.0;	//ok
    
    float f3 = 3.45f;
    float f4 = 6.8f;
    float f5 = f3 + f4;	//compile error:损失精度:不能将double型赋给float型
    float f6 = 3.45f+ f4;	//compile error:损失精度:不能将double型赋给float型
    float f7 = f3 + 6.8f;	//compile errorr:损失精度:不能将double型赋给float型
    float f8 = 3.45f + 6.8f;	//ok

字符型(char):

  • **概念:**Java中 char默认两字节,且 Java中内部编码默认采用 utf-16编码存储字符,所以,char类型存储的字符均为 utf-16的基本多文种平面BMP字符,要想存储Unicode字符集中除了基本多文种平面字符之外的字符(如:生僻字,表情符号,等),需要使用char数组或者String字符串存储。

  • 存储方式:

    • 通过字符存储:

      char c1 = 'a'; 		//ok
      char c2 = '汉';		//ok
      char c3 = '😊';		//error: 😊占据4字节,超出char型范围
    • 通过utf-16编码值存储(使用转义字符\u):

      char c1 = '\u0023';		//ok
      char c2 = '\u6C49';		//ok
      char c = '\uD83D\uDE0A';	//compile error: \uD83D\uDE0A占据4字节,超出char型范围
      
      //借助数组或者String类型来存储utf-16中的增补字符
      char[] cArray = new char[]{'\uD83D', '\uDE0A'};
      String str = new String("\uD83D\uDE0A");
    • 使用转义字符来存储特殊字符常量:

      char c1 = '\n';
      char c2 = '\t';

布尔型(boolean):

  • boolean类型只有 truefalse两种变量值,在编写时,只能赋值 truefalse两种变量值给 boolean类型变量,不能使用 0或者 1这种形式(不同于 C语言)
  • 拓展:Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达所操作的boolean值,在编译之后都使用java虚拟机中的int数据类型来代替:true用1表示,false用0表示。——《java虚拟机规范 8版》

<补充>: 基本数据类型之间运算规则:

  • 自动类型提升(自动):

    • 容量小的数据类型可以自动提升为容量大的数据类型(即:可以将容量下数据类型变量直接赋值赋值给容量大数据类型变量)

    • 规则:
      image-20250123152440631

    • 规则&示例:

      (1)当把存储范围小的值(常量值、变量的值、表达式计算的结果值)赋值给了存储范围大的变量时

      int i = 'A';//ok: char自动升级为int,即:将字符的utf-16编码值赋值给i变量了
      double d = 10;//ok: int自动升级为double
      long num = 1234567; //ok: int自动升级为long
      double d2 = 1.0f;	//ok: float自动升级为double

      (2)当存储范围小的数据类型与存储范围大的数据类型变量一起混合运算时,会按照其中最大的类型运算

      int i = 1;
      byte b = 1;
      double d = 1.0;
      
      int sumI = i + b + d;	//compile error: 损失精度:不能将double型赋给int型
      float sumF = i + b + d;	//compile error: 损失精度:不能将double型赋给float型
      double sumD = i + b + d;//ok:混合运算,升级为double
  • 强制类型转换(手动):

    • 若要避免上述的编译错误(Compile Error),就必须强制类型转换。使用强制类型转换可以避免程序在编译阶段(javac)不会报错,但是,强制类型转换可能会丢失精度,所以要进行判断之后来得出是否要强制类型转换。

    • 转换格式:

      数据类型1 变量名 = (数据类型1)被强转数据值;  //()中的数据类型必须<=变量值的数据类型

      (注意):当把存储范围大的值(常量值、变量的值、表达式计算的结果值)强制转换为存储范围小的变量时,可能会损失精度溢出

      int i = (int)3.14;//损失精度
      
      double d = 1.2;
      int num = (int)d;//损失精度
      
      int i = 200;
      byte b = (byte)i;//溢出
      
      //理想情况:
      byte b1 = 3;
      byte b2 = 4;
      byte b3 = (byte)(b1 + b2);	//ok: 安全转换
      
      float f = (float)1.0;		//ok: 安全转换

引用数据类型:

即:通过指针调用来索引数据(String类型,数组[]类型,等等..) Java中的引用数据类型隐藏指针的特性,使得我们不必显示的去定义指针(不同于 C语言)

  • Java(new 类名()):

    class Student{	//类:默认就是引用类型,所以不需要在显性的强调指针问题
        int id;
        String name;
        char gender;
    }
    
    public class Test{
        public static void main(String[] args){
            Student stu01 = new Student();	//使用“ new 类名() ”的方式,自动定义为引用类型
            stu01.id = 2020001;				//引用类型变量,再调取其内部成员变量时,可以采用“ 变量名.成员变量名 ”的方式
            stu01.name = "Tom";
            char gender = '男';
            
            System.out.println("ID:" + stu01.id + 
                               "\nName:" + stu01.name +
                               "\nGender:" + stu01.gender);
        }
    }
  • C语言((结构体名 *)malloc(sizeof(结构体名))):

    #include<stdio.h>
    
    typedef struct Student{
        int id;
        char * name;
        char gender;
    }Student, *PStudent;	//通过 * 的方式来显性的说明指针
    
    int main(void){
        //静态定义(不推荐):
        Student stu;
        stu.id = 2020000;
        stu01.name = (char*)malloc(sizeof(char)*3);
        stu01.name[0] = 'A';
        stu01.name[1] = 'd';
        stu01.name[2] = 'm';
        stu01.gender = 'F';
        
        //动态定义(引用类型):
        PStudent stu01 = (PStudent)malloc(sizeof(Student));	//通过 malloc方式动态分配内存,用过指针方式显性的去调用这块内存
        stu01->id = 2020001;	//使用“ 指针变量名->成员变量名 ”的方式调用动态内存中的成员变量
        stu01->name = (char*)malloc(sizeof(char)*3);
        stu01->name[0] = 'T';
        stu01->name[1] = 'o';
        stu01->name[2] = 'm';
        stu01->gender = 'M';
        
        printf("...");
        return 0;
    }

2.3 数组

2.3.1 一维数组:

数组基本概念:

  • 数组作为引用数据类型,其继承于Object类。不同其他引用类型(String类,自建的类,等等),数组作为引用类型,其并不会直接体现于Java源码中,也不用程序员手动定义,其由编译后至 JVM中运行,由 JVM自动生成声明的相关数据类型(基础/引用)的数组的类对象,并且设置成员变量 final int length;,用来记录对应数组实例化的对象的定长,且是 final属性,不可更改。Java中的数组一旦定义,其长度则不可被修改。在运行阶段,JVM会检查代码中的定义的数组是否出现越界问题。若出现越界数组则会抛出异常
    <示例>:以自定义引用数据类型 MyClass 类型 和 基本数据类型 int型 举例:

    //一般引用类,调用toString()输出哈希值:
    MyClass myClass = new MyClass();
    System.out.println(myClass);	//com.arrayexercise.exer01.MyClass@12a3a380
    
    //一维数组:
    MyClass[] myClasses = new MyClass[5];
    int[] intArray = new int[5];
    
    System.out.println(intArray);	//[I@12a3a380
    System.out.println(myClasses);	//[Lcom.arrayexercise.exer01.MyClass;@29453f44
    
    //二维数组:
    double[][] doubleArray = new double[3][5];
    String[][] strArray = new String[5][];
    
    System.out.println(doubleArray);	//[[D@5cad8086
    System.out.println(strArray);		//[[Ljava.lang.String;@6e0be858

    [Lcom.arrayexercise.exer01.MyClass;@29453f44进行说明:

    • [:表示为数组引用类型,[:一维数组,[[:二维数组,依次类推
    • L:表示以引用数据类型建立数组,若是以基本数据类型生成数组则不显示,如:int[]对应的: [I@12a3a380
    • com.arrayexercise.exer01.MyClass:表示建立该数组的原始引用数据类型的包名与类名,包名:com.arrayexercise.exer01,类名:MyClass
    • @29453f44:表示当前数组类型实例化的对象的地址哈希值。

如何定义:(以 int[] 为例)

  • 方式一: 静态定义:(由 JVM自动判断长度)

    int[] array1 = new int[]{1, 2, 3, 4, 5};
    
    //也可以先定义,后创建:
    int[] array2;
    array2 = new int[]{1, 2, 3, 4, 5};
  • 方式二: 动态定义(手动定义长度)

    Java中使用这种定义方式,JVM会自动初始化:整形:0,浮点型:0.0,字符型:\u0000,布尔型:false

    int[] array1 = new int[5];
    
    //也可以先定义,后创建:
    int[] array2;
    array2 = new int[5];

不论是方式一,还是方式二,一旦数组被定义且实例化创建,那么创建的数组的长度就确定了。

对比C语言:

  • C语言的数组也是指针引用类型,只不过是单纯的指针索引:

    int * array = (int*)malloc(sizeof(int) * 5);
    //C语言是单纯的指针索引,所以自身并没有length变量来记录数组的定长,需要手动计算
    int length = sizeof(array)/sizeof(array[0]);
    for(int i = 0; i < length; i++){
        visit(array[i]);
    }
  • Java中数组也作为引用类型,其采用一个类进行封装,且含有 length属性,其由 JVM依据创建对应数组的一开始长度来进行赋值,进行一个实例化数组对象长度的记录。

    int[] array = new int[5];
    
    for(int i = 0; i < array.length; i++){
        visit(array[i]);
    }

2.3.2 二维数组:

二维数组概念:

  • Java中数组同 C语言中的数组本质一样,只不过Java中引入了类添加了length属性,隐藏了指针。所以二维数组也是同理,C语言使用了二级指针索引一级的方式创建二维数组,Java中也是通过数组里面装数组的方式实现二维数组,只不过在 JVM帮我们创建数组类对象时,会将二维数组声明为[[型,如上面的:double[][] doubleArray = new double[3][5];对应的:[[D@5cad8086

    第05章_数组.jpeg

如何定义:(以 int[][] 为例)

  • 方式一: 静态定义:(由 JVM自动判断长度)

    int[][] array1 = new int[][]{{1, 2, 3, 4, 5},
                                 {5, 7, 8, 9, 10},
                                 {11, 12, 13, 14, 15}};
    
    //也可以先定义,后创建:
    int[][] array2;
    array2 = new int[][]{{1, 2, 3, 4, 5},
                         {5, 7, 8, 9, 10},
                         {11, 12, 13, 14, 15}};
  • 方式二: 动态定义(手动定义长度)

    Java中使用这种定义方式,JVM会自动初始化:整形:0,浮点型:0.0,字符型:\u0000,布尔型:false

    int[][] array1 = new int[3][5];
    
    //也可以先定义,后创建:
    int[] array2;
    array2 = new int[3][5];
    
    //这种前确定先确定二维数组长度的也可以:
    int[][] array3 = new int[3][];	//此时一维数组索引均为 null
    for (int i = 0; i < array3.length; i++){
        array3[i] = new int[5];
    }

不论是方式一,还是方式二,一旦数组被定义且实例化创建,那么创建的数组的长度就确定了。

对比C语言:

  • C语言的动态二维数组也是指针引用类型,只不过是单纯的指针索引:

    int ** array = (int**)malloc(sizeof(int*) * 3);
    int length01 = sizeof(array)/sizeof(array[0]);
    for(int i = 0; i < length01; i++){
        array[i] = (int*)malloc(sizeof(int) * 5);
    }
    int length02 = sizeof(array[0])/sizeof(array[0][0]);
    
    for(int i = 0; i < length01; i++){
        for(int j = 0; j < length02; j++){
            visit(array[i]);
        }
    }
  • Java中数组也作为引用类型,其采用一个类进行封装,且含有 length属性,其由 JVM依据创建对应数组的一开始长度来进行赋值,进行一个实例化数组对象长度的记录。

    int[][] array = new int[3][5];
    
    for(int i = 0; i < array.length; i++){
        for(int j = 0; j < array[i].length; j++){
            visit(array[i]);
        }
    }