Java 语法结构系统化总结

一、Java 的基本特性

  1. 跨平台性(Write Once, Run Anywhere)
    通过 Java 虚拟机(JVM)实现,编译后的字节码能够在任何安装了 JVM操作系统上运行。
    典型示例:在 Windows 平台开发的 Java 程序无需修改,即可在 LinuxmacOS上进行运行
    而 C/C++ 等语言需要针对不同平台重新编译。

  2. 面向对象特性
    封装:通过 privateprotectedpublic 修饰符控制访问权限
    继承:支持类的单继承以及接口的多继承
               *Java不能进行多继承 要实现多继承只能通过接口进行实现
    多态:包括编译时的重载和运行时的重写
               *重载发生在同一个类中,需要方法名一致,其参数顺序,内容不同才可发生重载
               *重写发生在继承体系中,其方法名与参数需与父类一致

  3. 自动内存管理垃圾回收机制
    JVM 会自动回收不再使用的对象所占内存,
    这一机制有效避免了 C/C++ 中常见的内存泄漏问题。

  4. 编码规范
    类名采用大驼峰命名法,如:MyClass
    方法和变量采用小驼峰命名法,如:myMethod


二、数据类型与变量

  • 基本数据类型 与 默认值

    • 在Java中基本数据类型(Primitive Data Types)共有8个,按照类型可以分为4大类:

    1. 整数类型

      类型说明取值范围默认值
      byte8位有符号整数-2^7  ---  2^7-1
      -128  ---  127
      0
      short16位有符号整数-2^15  ---  2^15-1
      -32,768   ---  32,767 
      0
      int32位有符号整数-2^31  ---  2^31-1
      -2147483648  ---  2147483647
      0
      long64位有符号整数-2^63  ---  2^63-1
      -9223372036854775808  ---  9223372036854775807
      0L
    2. 浮点类型

      类型说明精度默认值示例
      float32位单精度浮点数

      24 位二进制精度

      相当于 大约 6~7 位十进制有效数字

      0.0f

      float pi = 3.14159f;

      *float定义时需在末尾加上 f / F,否则编译器会视为double类型

      double64位双精度浮点数

      53 位二进制精度

      相当于 约 15~17 位十进制有效数字

      0.0ddouble pi = 3.141592653589793d;
      *后缀d可进行省略
    3. 字符类型

      类型说明可表示内容默认值示例
      char 

      16位Unicode字符

      *与C语言不一致,C语言位ASCII码

      可以表示从 '\u0000' 到 '\uffff' 的字符'\u0000'

      char grade = 'J';

    4. 布尔类型

      类型说明默认值示例
      boolean

      只有true和false两个值

      *需要注意,Java中没有0为假非0为真的说法

      false

      char grade = 'J';

      需要注意:在 Java 中,并不存在 “0 为假、非 0 为真” 的说法。也就是说,在使用 ifswitch 等用于逻辑判断的语句时,只能基于 true/false 进行判断不支持使用数字作为条件

  • String(字符串)属于引用类型 不属于基本数据类型

  • 变量的定义与作用域

    1. 变量,顾名思义,就是一个值可被改变的对象

    2. 作用域:

      1. 局部变量:当进入定义该变量的局部作用域时,变量被创建,并且只能在该作用域内使用;当退出该作用域时,变量随之销毁。

      2. 全局变量:从程序启动开始便存在,在整个程序运行期间始终有效,直到程序结束才被销毁

        public class Test {
            public static int A = 10;// 全局变量
            public static void main(String[] args) {
                int a = 10;//main方法的局部变量
                for (int i = 0; i < 10; i++) { // i 为 for循环的局部变量
                    int j =0;// j是for循环块内的局部变量
                }
            }
        }
  • 引用类型

    • 特点说明
      存储内容对象的内存地址(引用
      作用访问堆上的对象
      默认值null
      包含的类型类、接口、数组、枚举
      与基本类型的区别基本类型存储,引用类型存储引用(地址)
    • 内存分布图:

    • 引用类型赋值时,实际上是将新对象的地址赋给该引用变量进行存储。

      例如,下面的代码说明了这一点:

      public class Test4 {
          public static void main(String[] args) {
              String s1 = "Hello"; //这里为简写 完整内容为:String s1 = new String("hello");
              System.out.println(s1);
              s1 = "My Test";
              System.out.println(s1);
          }
      }
      

      运行结果:
               Hello
               My Test

    • 其在内存中的过程如下图所示:

  • 类型转换(自动 / 强制)

    这正是自动类型转换(即隐式类型转换)的体现,该过程由编译器自动完成,无需显式操作

    1. 强制类型转换
      使用 (目标类型) 对象名 的形式,即可实现显式类型转换

    2. 自动类型转换
      自动类型转换本质上与强制类型转换相同,都是对对象类型的转换。
      区别在于:强制类型转换是显式进行的,由程序员手动指定;而自动类型转换是隐式进行的,由编译器自动完成

      public class Test2 {
          public static void main(String[] args) {
              int intNumber = 42;
              double doubleNum = intNumber;  // 隐式类型转换发生在这里
              // double doubleNum = (double)intNumber;
              System.out.println("int 值: " + intNumber);
              System.out.println("转换后的 double 值: " + doubleNum);
          }
      }

      运行结果:
              int 值: 42
              转换后的 double 值: 42.0

  • final 关键字作用(变量不可变)

    • 基本数据类型

      • 当使用 final 修饰基本数据类型时,变量的值不可改变。
        需要注意的是,变量必须先完成初始化,未初始化时可以通过赋值进行初始化;
        一旦变量完成初始化后,该值便变为不可修改。

        public class Test3 {
            public static void main(String[] args) {
                final int a1 = 10;
                a1 = 10; // 编译报错,final变量不可修改
        
                final int a2;
                a2 = 10; // 正确,a2尚未初始化,完成初始化赋值
                a2 = 20; // 编译报错,a2已被初始化,不能再次修改
            }
        }

        final 修饰变量,就像是一把锁,将该对象锁定在一个“箱子”中,
        此时无法通过 变量名 = 新值 的方式对其进行修改。

    • 引用类型 

      • 当使用 final 修饰引用类型时,引用变量中存储的对象地址不可更改
        需要注意的是,虽然地址不能变,但该地址指向的对象内容仍可修改
        除非该类型本身是不可变的,例如 String 类型。

      • 参照下列代码:

        class Dog {
            String name;
            int age;
            double weight;
        
            public Dog(String name, int age, double weight) {
                this.name = name;
                this.age = age;
                this.weight = weight;
            }
        
            @Override
            public String toString() {
                return "Dog{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        ", weight=" + weight +
                        '}';
            }
        }
        public class Test5 {
            public static void main(String[] args) {
                final Dog dog = new Dog("小黄",8,14.5);
                //dog = new Dog("小黑",4,7);//报错 dog被final所修饰,不能进行更改
                System.out.println("修改前:" + dog.toString());
                dog.age = 10;
                dog.name = "阿黄";
                dog.weight = 20;
                System.out.println("修改后:" + dog.toString());
            }
        }

        运行结果:
        修改前:Dog{name='小黄', age=8, weight=14.5}
        修改后:Dog{name='阿黄', age=10, weight=20.0}

          由此可见,当使用 final 修饰引用类型时,确实是使引用变量本身不可变,即其存储的对象地址不能更改,但该地址所指向的对象内容仍然可以被修改


三、运算符与表达式

  • 算术、赋值、关系、逻辑运算符

    • 算数运算符

      • 基本四则运算符:

        运算符说明注意事项
        +加法运算符,用于两数求和
        -减法运算符,用于求两数之差
        *乘法运算符,用于求两数之积
        /除法运算符,用于求两数之比除数不能为 0
        %求余运算符,用于求余数可对小数取模,但不能对 0 取模
      • 增量运算符

        运算符示例(以 a, b 为参数)等价于(以 a, b 作为参数)注意事项
        +=a += ba = a + b
        -=a -= ba = a - b
        /=a /= ba = a / bb 不能为 0
        *=a *= ba = a * b
        %=a %= ba = a % bb 不能为 0,且可为小数
      • 自增/自减 运算符
        运算符说明示例(以a为参数)等价于(以a作为参数)
        ++前置++++aa = a +1
        后置++a++a = a +1
        --前置----aa = a - 1
        后置--a--a = a -1
        前置++(--)与后置++(--)区别
        说明区别
        前置++先将变量进行 +1,然后再参与后续的运算操作(如求积等)
        后置++先参与运算操作,之后再执行 +1
        前置--先将变量进行 -1,然后再参与后续的运算操作
        后置--先参与运算操作,之后再执行 -1
    • 关系运算符
      • 运算符示例(以a,b为参数)返回值(假设a =10,b = 20)注意事项
        ==a == bfalse不能连续 等于(a==b==c)
        !=a != btrue不能连续 不等于(a != b != c)
        >a > bfalse不能连续 大于 (a > b > c)
        >=a >= bfalse不能连续 >= (a >= b >= c)
        <a < btrue不能连续 小于(a < b <c)
        <=a <= btrue不能连续 <= (a >= b >= c)
    • 逻辑运算符

      • 逻辑运算符主要包含:||(或)&&(与)!(非)
        其运算结果均为布尔类型(truefalse

      • 运算符说明表达式1表达式2返回值
        &&全真为真 有假则为假
        ||有一个为真则为真
        将结果取反表达式返回值
      • ||&& 运算中,存在短路求值的情况。

        例如表达式:10 < 20 || 10 / 0
        虽然 10 / 0 会导致除以零错误,但程序依然输出 true,这是因为短路求值生效了。

        所谓短路求值,理解起来非常简单:

        • 对于 &&:如果左侧表达式为 false,右侧就不会再执行,直接返回 false

        • 对于 ||:如果左侧表达式为 true,右侧就不会再执行,直接返回 true

    • 位运算符

      • 位运算符操作的是二进制位

        常用的位运算符包括:&(按位与)|(按位或)~(按位取反)^(按位异或)

      • 按位与 &

        • 全 1 输出 1 ,有 0 则输出 0

        • 用途:一般用于清零操作,比如需要将 A(10110011)进行清零,那么将A与00000000进行按位与操作,则A就可以被清零

        • 表达式表达式A表达式B结果
          A & B111
          000
          100
          010
          示例:
          表达式A1011 1001 1111
          表达式B1001 0111 0011 
          结果1001 0001 0011 
      • 按位或 |

        • 有 1 输出 1,全 0 输出 0

        • 用途:常用于设置某些位为 1,如 A 为 00000000,想将第三位设为 1,让 A 与 00000100 按位或,A 的第三位就会变为 1 。

        • 表达式表达式A表达式B结果
          A | B111
          101
          011
          000
          示例:
          表达式A1011 1001 1111
          表达式B1001 0111 0011 
          结果1011 1111 1111 
      • 按位取反 ~

        • 将对应位进行取反操作,即 0 变为 1,1 变为 0

        • 表达式表达式B结果
           ~ B10
          01
          示例:
          表达式B1001 0111 0011 
          结果0110 1000 1100
      • 按位异或 ^

        • 两位相同为0 ,不同为1

        • 用途:用于识别唯一出现的数字。例如:A(1001)、B(1001)、C(0101),可以通过以下步骤进行异或运算:

          • 先计算 A^B = 0000
          • 再将结果与 C 异或:0000 ^ 0101 = 0101 最终结果 0101 即为唯一出现的数字 C。
        • 表达式表达式A表达式B结果
          A ^ B111
          001
          010
          100
          示例:
          表达式A1011 1001 1111
          表达式B1001 0111 0011 
          结果0010 1110 1100
      • 总结:
      • 名称符号规则应用层面
        按位与&如果两个二进制位都为 1,则结果为 1,否则为 0用于清零掩码操作(保留特定位)、权限控制
        按位或|如果两个二进制位中有一个为 1,则结果为 1,否则为 0用于设置某些位为 1合并标志位
        按位取反~如果该位为 0,结果为 1;为 1,结果为 0用于取反生成补码快速计算 -x - 1
        按位异或^两位相同为 0不同为 1用于比较差异加密解密不使用临时变量交换两个数

    • 位移运算

      • 位移运算符共有三种,分别是:<<(左移)>>(右移)>>>(无符号右移)
        它们的操作对象都是二进制位

      • 左移运算符(>>)
        • 左移运算符会将二进制数的每一位向左移动指定的位数,高位溢出部分舍去低位用 0 补齐

        • 格式

          • B << X
            
          • B:二进制数

          • X:需要移动的位数

        • 示例:
          1000 1100 左移 1 位

          操作过程:

          • 左移1000 1100 << 1

          • 低位补 0 1 0001 1000

          • 舍弃溢出位0001 1000

          • 最终结果0001 1000

        • 过程动画演示:

      • 右移运算符(>>)

        • Java 中,>> 也被称为 算术右移(带符号右移)

        • 右移运算符会将二进制数的每一位向右移动指定的位数,高位空出的位置由符号位填充

        • 格式

          • B >> X
          • B:二进制数

          • X:需要移动的位数

        • 示例:
          1000 1100 右移 3 位

          操作过程:

          • 右移1000 1100 >> 3

          • 高位补符号位(1)1111 0001 100

          • 舍弃溢出位1111 0001

        • 最终结果1111 0001

        • 过程动画演示:

      • 无符号右移 >>>

        • 在 Java 中,>>> 也被称为 逻辑右移(无符号右移)

        • 无符号右移运算符会将二进制数的每一位向右移动指定的位数,高位空出的位置统一补 0(不考虑符号位)。

        • 格式:

          • B >>> X
            
            • B:二进制数

            • X:需要右移的位数

      • 示例:
        1000 1100 右移 3 位

        操作过程:

        • 右移1000 1100 >> 3

        • 高位补 0(无符号右移不补符号位) → 0001 0001 100

        • 舍弃溢出位0001 0001

      • 最终结果0001 0001

      • 过程动画演示:
      • 注意:Java中没有无符号左移!!

    • 条件运算符

      • 条件运算符只有一个:a ? b : c

      • 表达式1 ? 表达式2 : 表达式3

        • 表达式1 的值 为 true 的时候,整个表达式的值为 表达式2 的值

        • 表达式1 的值 为 false 的时候,整个表达式的值为 表达式3 的值

      • 该条件运算符也是Java中唯一一个三目运算符

      • 注意事项:

        • 表达式2表达式3 需要是相同类型的数据
          除非它们之间能够发生自动类型转换(即隐式类型转换),
          否则将会导致类型不匹配的编译错误

          • public static void main(String[] args) {
                    int a = 10;
                    int aa = 20;
                    double b = 20.0;
                    String s = "abc";
                    System.out.println(a > aa ? a : aa);//同类型
                    System.out.println(a > b ? a : b);//发生隐式转换
                    //System.out.println(a > s ? a : s);// a > s部分报错 类型不匹配
                }

            运行结果:
                    20
                    20.0

        • 表达式必须被使用,不能单独出现

          • public static void main(String[] args) {
                    int a = 10;
                    int b = 20;
                    a > b ? a : b;//报错 该表达式必须被使用
                }
    • 运算符的优先级

运算符优先级
类型符号说明优先级(1~12)
括号与成员访问() 方法调用/强制类型转换1
[ ]数组访问
.成员访问
单目运算符++后置++
--后置--
+(正号)表示一个数为正数2
-(负号)表示一个数为负数
~(按位取反)将 1 变为0,0 变为 1
同属于位运算符
!(逻辑非)将true 变 false ,false 变 true
同属于逻辑运算符
++前置++
--前置--
算术运算符*乘法3
/除法
%取模
+(加法)求两数之和4
-(减法)求两数之差
位移运算符<<左移5
>>右移(带符号右移)
>>>无符号右移
关系与相等运算符<判断两数大小6
>
<=
>=
==判断两数是否相等7
!=判断两数是否不想等
位运算符&全 1 输出 1 ,有 0 输出 08
^相同 输出 0,不同输出 19
|有 1 输出 1,全 0 输出010
逻辑运算符&&判断左右两边条件是否均成立11
||判断左右两边是否有条件成立12
三元运算符? : 简化版本的if else13
赋值运算符=进行赋值操作14
+=将数值相加后赋值
-=将数值相减后赋值
*=将数值自乘后赋值
/=将数值自除后赋值
%=将数值自模后赋值
<<=左移后赋值
>>=右移后赋值(带符号右移)
>>>=无符号右移后赋值
&=按位与后  赋值
^=按位取反后  赋值
|=按位或后  赋值
分隔符,用于分隔 变量 方法参数等15
  • ' + ' 在字符串上的特别应用

    • 使用 ' + ' 可以完成字符串拼接

    • public static void main(String[] args) {
              String a = "hello";
              String b = " Java";
              System.out.println(a + b);
          }

      运行结果:
              hello Java


四、流程控制语句

  • if-elseswitch 的使用场景和陷阱(如:case 穿透)

    • if-else 

    • 格式:

      • if (条件语句) {
                    // 代码块
                } else if (条件语句) {
                    //代码块
                } else {
                    //代码块
                }

        注意事项:

      1. 条件语句的结果必须是布尔类型

      2. 满足哪个条件,就会进入该条件对应的代码块,执行相应的代码

    • switch

      • 格式:

        switch (value) {
                    case 1:
                        // 代码块
                    case 2:
                        // 代码块
                    case 3:
                        // 代码块
                    default:
                        // 代码块
                }
      • 注意事项:

      1. case 后只能跟编译时常量,并且其类型必须与 switch 表达式的类型一致

      2. 当进入 switch 后,会选择与 case 后的值相同的分支进入

      3. switch 中的表达式不能是浮点型(floatdouble)、longboolean 或自定义类型

      • case 穿透:

        当从某个 case 进入后,代码会一直向下执行,直到遇到 break 或执行完所有的 case default 代码块
        这种现象称为case 穿透

        动图展示(展示有无 break 时的区别):

        下面是中途有 break 的情况下:

  • 循环结构:forwhiledo-while

    • for 循环

      • 格式:

        • for (初始话语句;循环条件;迭代语句) {
                      //循环体
                  }
        • 初始化语句:设置循环的起点,声明并初始化循环控制变量。此部分仅在循环开始时执行一次

        • 循环条件:每次循环前检查的布尔表达式,若为 true 执行循环体,若为 false 则终止循环

        • 迭代语句:每次循环结束后更新循环控制变量,通常为递增/递减操作

      • for 的变体:for each

        • for each 主要用于操作数组或集合。

          • 其格式如下:

          • for (元素类型 变量名 : 数组或集合) {
                        // 循环体
                    }

          • 元素类型:必须与数组或集合中的元素类型一致

          • 变量名:临时变量,表示当前迭代的元素

          • 终止条件:当所有元素遍历完成后,循环结束

          • 运行逻辑:将数组或集合中的元素一个一个赋值给 变量名,然后执行循环体中的相关代码。例如,以下代码:

            •  public static void main(String[] args) {
                      int[] arr = {1,2,3};
                      for (int tmp :arr) {
                          System.out.println(tmp);
                      }
                  }
            • 运行结果:
                      1
                      2
                      3

            • 运行过程:

              1. 执行 tmp = arr[0],然后开始执行循环体

              2. 更新 tmp = arr[1],继续执行循环体

              3. 更新 tmp = arr[2],再次执行循环体

              4. 完成数组遍历后,循环结束

      • while 循环

        • public static void main(String[] args) {
          while (条件判断语句) {
                      //循环体
              }
          }
          • 当满足条件时,进入循环并重复执行,直到条件不再符合为止

          • 示例:

            • public static void main(String[] args) {
                      int left = 0;
                      int right = 200;
                      while (left++ < right--) {
                          System.out.print(left + " ");
                      }
                      /*while (条件判断语句) {
              
                      }*/
                  }

              功能说明:
              该代码打印了 1 到 100 的所有数字(示例代码仅为演示,不具实际意义)

          • 注意:
            条件判断语句的返回值必须是布尔类型

      • do - while 

        • do - whilewhile 的主要区别在于,do - while 无论条件是否成立都会先执行一次循环体,然后再进行条件判断,决定是否继续执行循环。后续操作与 while 一致,因此不做过多介绍

        • 格式:

          • do{
                        //循环体
                    } while (条件判断语句);
          • 需要注意:while 后面需要加上分号 ;

  • 跳转语句:breakcontinuereturn 的语义区别

    • break:立即终止当前的循环或 switch 语句,跳出所在的代码块。

      • 示例​:

      •  public static void main(String[] args) {
                for (int i = 0; i < 10; i++) {
                    if (i == 5) {
                        break;
                    }
                    System.out.printf("%d ",i);
                }
            }

        运行结果:
                0 1 2 3 4 

    • continue跳过本次循环的剩余代码,直接进入下一次循环的条件判断

      • 示例​:

      •  public static void main(String[] args) {
                for (int i = 0; i < 10; i++) {
                    if (i == 5) {
                        continue;
                    }
                    System.out.printf("%d ",i);
                }
            }
      • 运行结果:
        0 1 2 3 4 6 7 8 9 

    • return终止当前方法的执行,并返回一个值(如果方法有返回值)

      • 示例:

      • public static void main(String[] args) {
                for (int i = 0; i < 10; i++) {
                    if (i == 5) {
                        return;
                    }
                    System.out.printf("%d ",i);
                }
            }

        运行结果:
                0 1 2 3 4 

    • 注意事项:

      • returnbreak continue 语句后,不能写后续代码,否则会编译报错。以下是三个常见的错误示例:

      • public static void main(String[] args) {
                System.out.println(1);
                return;
                System.out.println(2);//编译报错
            }
      • public static void main(String[] args) {
                for (int i = 0; i < 5; i++) {
                    if (i == 3) {
                        break;
                        System.out.println(i);//编译报错
                    }
                }
            }
      • public static void main(String[] args) {
                for (int i = 0; i < 5; i++) {
                    if (i == 3) {
                        continue;
                        System.out.println(i);//编译报错
                    }
                }
            }

五、数组

  • 一维数组

    • 一维数组的定义:

      • 数组:数组是用于存储一类相同数据类型的元素,通过连续的内存空间下标索引进行访问

      • 一维数组的定义 有两种方法:

        • 元素类型[] 数组名;
          元素类型 数组名[];//不推荐

          第一种方法更为常用,其优势在于表达更清晰,原因如下:

        • 使用第二种方式定义时,按照从左到右的顺序,无法立即判断出这是一个数组

        • 而使用第一种方法时,看到元素类型后的 [] 符号,就能立即识别出这是一个数组


          单独比较这两种定义方式:

        • 在查看第二种定义方式时,虽然看到数组名后的 [] 可以判断出这是一个数组,但无法立即知晓数组存储的数据类型

        • 相比之下,第一种方法在显示 [] 符号的同时,能够清晰地展示数组的元素类型

    • 初始化

      • 一维数组的初始化一般有以下几种方式:

      1. 静态初始化(简化版):

        int[] arr1 = {1,2,3,4,5};
        //创建了一个数组大小为5 内容为 1 2 3 4 5的数组
      2. 静态初始化(完整版本):

        int[] arr2 = new int[] {1,2,3,4,5};
        //创建了一个数组大小为5 内容为 1 2 3 4 5的数组
      3. 动态初始化

        int[] arr3 = new int[5];
        //创建了一个数组大小为5 内容都为0的数组
      • 其在内存中分配情况如下:

    • 一维数组的遍历

      • 数组的访问是通过索引(下标)进行的

        • 格式:数组名.[索引]

      • 通过数组的下标,我们可以轻松遍历整个数组:

      • public static void main(String[] args) {
                int[] arr = {1,2,3,4,5,6,7,8,9,10};
                for (int i = 0; i < arr.length; i++) {
                    System.out.print(arr[i] + " ");
                }
            }

        运行结果:
                1 2 3 4 5 6 7 8 9 10 

  • 简单算法练习(冒泡排序)

    • 冒泡排序方法核心:

      • 逐一比对数组中相邻两个元素的大小,如果 arr[0] > arr[1],则交换两者的位置

      • 动画演示:

         

    • 现有一个待排序的数组,编写一个冒泡排序方法对其进行排序:

    • import java.util.Arrays;
      public class Test1 {
          public static void mySort(int[] arr) {
              int len = arr.length;
              for (int i = 0; i < len - 1; i++) {
                  for (int j = 0; j < len - i - 1; j++) {
                      if (arr[j] > arr[j+1]) {
                          int tmp = arr[j];
                          arr[j] = arr[j+1];
                          arr[j+1] = tmp;
                      }
                  }
              }
          }
          public static void main(String[] args) {
              int[] arr = {1,2,9,3,8,4,7,5,6};
              System.out.println("排序前:" + Arrays.toString(arr));
              mySort(arr);
              System.out.println("排序后:" + Arrays.toString(arr));
      
          }
      }
      
  • 二维数组

    • 二维数组本质上仍然是一维数组,它是用来存放一维数组的数组

    • 定义格式:

      元素类型[][] 数组名称;;
      元素类型 数组名[][];//不推荐
    • 示例

      public static void main(String[] args) {
              int[] arr1 = {1,2,3};
              int[] arr2 = {2,3,4};
              int[] arr3 = {3,4,5};
              int[][] arrS1 = {arr1,arr2,arr3};
              int[][] arrS2 = {{1,2,3},{2,3,4},{3,4,5}};
              System.out.println("arrS1:");
              System.out.println(Arrays.toString(arrS1));
              System.out.println(Arrays.deepToString(arrS1));
              System.out.println("arrS2");
              System.out.println(Arrays.toString(arrS2));
              System.out.println(Arrays.deepToString(arrS2));
          }
    • 运行结果:
      arrS1:
      [[I@4c873330, [I@119d7047, [I@776ec8df]
      [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
      arrS2
      [[I@3b07d329, [I@41629346, [I@404b9385]
      [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

    • 图解内存空间:

  • Arrays.toString()Arrays.sort() 工具类简介

    • Arrays.toString()

      • 使用 Arrays.toString() 时需要导入相应的包java.util.Arrays

      • 作用Arrays.toString() 用于将一维数组转换为格式化的字符串

        • 格式[元素1, 元素2, ..., 元素N],每个元素之间使用逗号和空格分隔,且用方括号包围

        • 支持类型:适用于所有基本数据类型对象类型的一维数组

      • 使用实例:

      • import java.util.Arrays;//使用Arrays.toString()需导入该包
        
        public class Main {
            public static void main(String[] args) {
                int[] numbers = {1, 2, 3, 4, 5};
                String arrayString = Arrays.toString(numbers);
                System.out.println(arrayString); // 输出: [1, 2, 3, 4, 5]
            }
        }

        输出结果:
                [1, 2, 3, 4, 5]

    • 如果遇到二维数组,则需要使用 Arrays.deepToString() 方法

      • 使用示例:

        import java.util.Arrays;//使用Arrays.toString()需导入该包
        
        public class Test {
            public static void main(String[] args) {
                int[][] numbers = {{1,2,3},{2,3,4},{3,4,5}};
                System.out.println("Arrays.toString()");
                System.out.println(Arrays.toString(numbers));
                System.out.println("===========");
                System.out.println("Arrays.deepToString()");
                System.out.println(Arrays.deepToString(numbers));
            }
        }

        输出结果:
        Arrays.toString()
        [[I@4c873330, [I@119d7047, [I@776ec8df]
        ===========
        Arrays.deepToString()
        [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

        通过输出可以看到,如果对二维数组使用 Arrays.toString(),则无法正确输出数组中的内容。相反,使用 Arrays.deepToString() 就可以正确地输出二维数组的所有内容

    • Arrays.sort()

      • 作用:用于对一维数组进行快速排序。根据不同的数据类型,Arrays.sort() 会自动选择最适合的排序算法进行排序

      • 使用示例:

      • public class Test3 {
            public static void main(String[] args) {
                int[] arr = {2,3,1,54,7,12,534,6};
                String[] name = {"lis","Aril","Ashy","Nike","Peas"};
                Arrays.sort(arr);
                System.out.println(Arrays.toString(arr));
                Arrays.sort(name);
                System.out.println(Arrays.toString(name));
            }
        }
        

        运行结果:
                [1, 2, 3, 6, 7, 12, 54, 534]
                [Aril, Ashy, Nike, Peas, lis]

      • 其次,Comparable 接口也可以实现自定义排序方式:

        假设有如下场景:
        有一个 group 对象,该对象中存放了小组中每个人的姓名、年龄、性别等信息。如果要按照其中某一个属性进行排序,显然,使用默认的 Arrays.sort() 方法是不可取的。这时,我们就需要使用自定义的排序方式,例如:

        • class Group implements Comparable<Group>{
          //  需要自定义排序方式时 需要实现Comparable接口 
          //  <>内代表的是需要对比的对象
              String name;
              String sex;
              int age;
          
              public Group(String name, String sex, int age) {
                  this.name = name;
                  this.sex = sex;
                  this.age = age;
              }
          
              @Override
              public int compareTo(Group o) {
                  return this.age - o.age;
              }//重写 compareTo 方法
          
              @Override
              public String toString() {
                  return "Group{" +
                          "name='" + name + '\'' +
                          ", sex='" + sex + '\'' +
                          ", age=" + age +
                          '}';
              }//重写 toString 方法
          }
        • 测试代码:
           

          import java.util.Arrays;
          public class Test {
              public static void main(String[] args) {
                  Group Lisa = new Group("Lisa","girl",18);
                  Group Ail = new Group("Ail","girl",21);
                  Group Alonzo = new Group("Alonzo","boy",20);
                  Group[] groups = {Lisa,Ail,Alonzo};
                  Arrays.sort(groups);
                  System.out.println(Arrays.toString(groups));
              }
          }
          class Group implements Comparable<Group>{
          // 需要自定义排序方式时 需要实现Comparable接口
          // <>内代表的是需要对比的对象
              String name;
              String sex;
              int age;
          
              public Group(String name, String sex, int age) {
                  this.name = name;
                  this.sex = sex;
                  this.age = age;
              }
          
              @Override
              public int compareTo(Group o) {
                  return this.age - o.age;
              }//重写 compareTo 方法
          
              @Override
              public String toString() {
                  return "Group{" +
                          "name='" + name + '\'' +
                          ", sex='" + sex + '\'' +
                          ", age=" + age +
                          '}';
              }
          }
          
          

          运行结果:
                  [Group{name='Lisa', sex='girl', age=18}, Group{name='Alonzo', sex='boy', age=20}, Group{name='Ail', sex='girl', age=21}]

        • 注意事项:

        1. 如果需要实现自定义排序规则,则必须实现 Comparable 接口

        2. Comparable 后面的 <> 中,填写需要比较的类型

        3. 在重写的 compareTo 方法中,决定了排序方式:是按升序还是降序排列


六、方法的使用与机制

  • 方法的定义、调用、参数传递规则

    • 方法的定义

      • 格式:

        访问权限 返回类型 方法名称(参数) {
                //方法体
            }
    • 方法的传参

      • 在括号 () 内填写需要传入的参数,这里的参数称为形参,它是实参的一份临时拷贝。

      • 示例代码

        class Test{
            public void instance(int tmp) {
                tmp = 10;
            }
        }
        public class Test2 {
            public static void main(String[] args) {
                Test test = new Test();
                int a = 20;
                test.instance(a);
                System.out.println(a);
            }
        }

        该代码中,最终输出仍然是 20。这说明调用 instance 方法并没有改变变量 a 的值。原因在于,方法中改变的是形参 tmp,而不是实参 a

        可以将其类比为:你有一份卷子 A,你将它复印了一份,然后在复印件上做了标记。无论你如何修改复印件,原始的卷子 A 都不会受到影响

        那么,计算机内部是如何处理这种情况的呢?
        下面是一个图解:


    • 普通方法

      • 普通方法的调用

        • 外部调用(外部类或 main 中调用)需要先实例化对应的类,然后通过实例使用点运算符(.)来调用普通方法。
          例如:obj.method()

        • 内部调用(同一类内调用)在同一类内部,实例方法可以直接调用本类的其他实例方法(无需实例引用)。
          但如果调用方法所在的上下文是 static 方法,则 不能直接调用实例方法,必须通过实例引用来调用(或把被调用的方法也声明为 static,但要注意语义是否合适)。

      • 示例:

        class Greet{
            public void Greet() {
                System.out.println("hello word");
            }
        }
        public class Test1 {
            public static void main(String[] args) {
                Greet greet = new Greet();
                greet.Greet();
            }
        }
        

    • 静态方法

      • 静态方法定义时需要使用关键字 static

      • 格式:

        访问权限 static 返回类型 方法名称() {
                //方法体
            }

      • 静态方法的调用 
        • 外部调用(类外):使用 类名.方法名() 进行调用。
        • 内部调用(同一类内)

          • 在静态方法内部可以直接调用同类的其他静态方法(无需类名)。

          • 在实例方法内部也可以直接调用静态方法(静态方法属于类级别,对实例可见)。

          • 注意:静态方法不能直接访问实例变量或实例方法,如需访问必须通过对象引用。

      • 示例:
        class Test {
            public static void Tests() {
                System.out.println("Tests()");
            }
            public static void test2() {
                Tests();
            }
        }
        public class Test1 {
            public static void main(String[] args) {
                Test.Tests();
                Test.test2();
            }
        }

        运行结果:
                Tests()
                Tests()

  • 静态方法与实例成员访问的简易示意说明:

    • 静态方法属于类级别,它在内存中只有一份,所有实例共享。

    • 实例成员属于对象级别,每个对象有自己的独立拷贝。

    • 因此,静态方法无法直接访问对象的实例成员(因为它不知道是哪个对象的成员),只能通过明确的对象引用来访问。

    • 实例方法属于某个对象,可以访问该对象的所有成员(静态的和实例的)。

  • 返回值与 void

    • 方法的返回值

      • 先通过一段代码来了解什么是返回值以及它的作用:

        public class Test2 {
            public static int Add(int a,int b) {
                return a+b;
            }
        
            public static void main(String[] args) {
                int a = 10;
                int b = 20;
                System.out.println(Add(a, b));
            }
        }

        运行结果:
                30

      • 通过这段代码可以看到,main 方法调用了 Add 函数,并将变量 ab 的值传递给它。此时,Add 方法中的 ab 是形参。

        计算完成后,Add 方法如何将 a + b 的结果告诉 main 方法呢?

        答案是:通过 return 关键字将计算结果返回给调用者

        也就是说,调用该方法的地方(这里是 main)会接收到 return 语句后返回的值。

      • 图解(仅为直观示意,不代表真实内存结构):

    • void 

      • void 关键字表示方法没有返回值,即方法执行完毕后不需要向调用者返回任何数据。

        简单来说,使用 void 修饰的方法不通过 return 语句返回值,只能执行方法体内的操作(比如打印、修改对象状态等)

      • 示例:

        public class Demo {
            // 一个没有返回值的方法,直接打印内容
            public static void printMessage() {
                System.out.println("这是一个void方法,没有返回值");
            }
        
            public static void main(String[] args) {
                printMessage();  // 调用void方法,不接收任何返回值
            }
        }
        

        在调用 void 方法时,不能将它作为表达式的一部分,也不能用变量接收它的返回值,否则编译会报错。

  • 方法重载(Overload)概念与实战例子

    • 概念:当调用同一个方法的时候,对于不同的参数可以使用不同的运行逻辑。

              方法重载指的是在同一个类中,允许多个方法名称相同,但参数列表不同(参数个数或类型顺序不同),以实现根据不同参数执行不同逻辑的一种机制。

      换句话说,调用同一个方法名时,根据传入参数的不同,Java会自动选择匹配的方法执行。

    • 为什么需要方法重载?

      假设我们要实现以下需求:

      • 计算两个整数的和

      • 计算三个浮点数的和

      • 计算两个浮点数的

      • 计算三个整数的和

    • 如果为每种情况都写一个不同名字的方法,会导致方法名臃肿、调用不方便。

      方法重载则可以让我们只用一个方法名,通过不同的参数组合实现多种计算逻辑,代码更加简洁清晰。

    • 方法重载的实现条件

    1. 方法名必须相同

    2. 参数列表不同,包括:

      • 参数个数不同;

      • 或参数类型顺序不同(需要注意:当参数类型完全相同且数量为2时,交换参数顺序不构成重载,编译器会报错;只有参数个数≥3且顺序不同才算有效重载)。

    3. 方法必须定义在同一个类中

    • 示例:

      public class Test3 {
          public static int Add(int x,int y) {
              return x+y;
          }
          public static double Add(int x,int y,double d) {
              return x+y+d;
          }
          public static double Add(int x,double d,int y) {
              return x+d+y;
          }
          public static int Add(int x,int y,int z) {
              return x+y+z;
          }
          public static void main(String[] args) {
              int x = 10;
              int y = 20;
              double d = 41.1;
              int z = 30;
              System.out.println(Add(x, y));
              System.out.println(Add(x, y, d));
              System.out.println(Add(x, d, y));
              System.out.println(Add(x, y, z));
          }
      }

      运行结果:
              30
              71.1
              71.1
              60

  • static 方法与普通方法的区别

    区别static 方法普通方法
    访问权限只能访问类的静态成员,不能访问非静态成员可以访问类中的所有成员(静态和非静态)
    调用方式不同通过类名直接调用通过实例化对象进行调用
    关键字区别需要使用static关键字修饰方法不需要使用static进行修饰
    所属关系不同属于属于实例对象
    声明周期不同随类的加载而存在,整个程序运行期间只有一份随对象的创建和销毁而存在


七、面向对象

  • 类与对象的定义与使用

    • 类的定义
              类是对一类事物的抽象描述,是具有相同属性和行为的对象的集合,它定义了对象的属性(成员变量),行为(成员方法)

    • 格式:

      修饰符 class 类名 {
          // 成员变量(属性)
          // 成员方法(行为)
      }
      
    • 对象的定义:       
              对象是类的实体,可以通过对象访问类中定义的属性和方法

    • 对象的创建:
              通过 new 关键字创建对象
      格式:

      类名 对象名 = new 类名();
    • 类与对象的关系

      • 类是抽象的模板,用来描述事物的共性特征

      • 对象是类的具体实例,拥有类中定义的成员属性成员方法

      • 可以将类看做是一个模板,而对象通过该模板进行创建,创建的内容必须包含目标中的内容,模板以外的内容自由发挥

  • 成员变量 vs 局部变量

    • 成员变量是定义在类中的变量,属于对象或类本身。

    • 局部变量是定义在方法、构造器或代码块中的变量,只在该范围内有效。

  1. 初始化要求

    • 成员变量不需要显式初始化,系统会自动赋予默认值(如整型默认是0,布尔型是false)。

    • 局部变量必须显式初始化后才能使用,否则编译器会报错。

  2. 生命周期

    • 成员变量随着对象的生命周期存在,直到对象被销毁才释放。

    • 局部变量随着所在方法或代码块的执行结束而销毁,生命周期较短。

  3. 作用范围

    • 成员变量的作用域是整个类内,可以被类中所有非静态方法访问。

    • 局部变量的作用域仅限于声明它的方法或代码块内部。

  • 构造方法:默认构造器、自定义构造器

    • 构造方法一般是用于进行成员的初始化

    • 格式如下:

      • public 类名(参数) {
            //进行成员的初始化
        }
    • 示例:

      class A {
          private int age;
          private String name;
      
          public A(int age, String name) {
              this.age = age;
              this.name = name;
          }
      }

      在编译器中,通常会提供快速生成构造方法的功能。
      IntelliJ IDEA 2022.1.4 为例,可以通过 Alt + Insert 快捷键唤出代码生成窗口,如下所示:

    • 按照上述步骤在窗口中进行操作后,编译器会自动生成对应的构造方法,无需手动编写,大幅提升编码效率。

  • this 关键字的实际用途

    • 当方法的参数名与类中的成员变量名发生冲突时,可以使用 this.成员名 来明确指代类的成员变量。
      这里的 this 代表当前对象的引用this.name 表示 “当前对象的 name 成员变量”

    • 示例

      public class Person {
          String name;
      
          public Person(String name) {
              this.name = name; // this.name 表示成员变量,右侧 name 表示构造方法的参数
          }
      
          public void showName() {
              System.out.println("我的名字是:" + this.name);
          }
      }
      
  • 封装与访问修饰符

    • 封装是面向对象的基本原则之一,指将类的属性和实现细节对外隐藏,只通过受控的接口(方法)暴露行为
      打个比方:计算器对外只提供按键和显示屏,我们看不到内部的实现细节,这就是封装的思想。

    • 在进行封装之前需要了解访问修饰限定符

    • 访问修饰限定符
      关键字范围访问权限(1-4,数字越大,越开放)
      同一包中的同一类同一包中的不同类不同包中的子类不同包中的非子类
      private×××1
      default××2
      protected×3
      public4
    • 一般情况下成员变量用 private,对外接口(方法)用 public

      • 格式:

        访问修饰限定符 数据类型 成员名称;
    • 下列是一个使用封装 定义的计算器类 的示例

      class Calm {
          private int num1;
          private int num2;
      
          public int getNum1() {
              return num1;
          }
      
          public void setNum1(int num1) {
              this.num1 = num1;
          }
      
          public int getNum2() {
              return num2;
          }
      
          public void setNum2(int num2) {
              this.num2 = num2;
          }
      
          public int Add() {
              return this.num1 + this.num2;
          }
      
          public int Sub() {
              return this.num1 - this.num2;
          }
          public int Mul() {
              return this.num1 * this.num2;
          }
          public double Div() {
              return (double)this.num1 / (double)this.num2;
          }
      }
      public class Test3 {
          public static void main(String[] args) {
              Calm calm = new Calm();
              calm.setNum1(10);
              calm.setNum2(20);
      
              System.out.println("加法 " + calm.Add());
              System.out.println("减法 " + calm.Sub());
              System.out.println("除法 " + calm.Div());
              System.out.println("乘法 " + calm.Mul());
          }
      }
      

      运行结果:
              加法 30
              减法 -10
              除法 0.5
              乘法 200

  • 继承(Inheritance)

    • 继承是面向对象编程中的三大特性之一,用于描述类与类之间的“是一个”(is-a)关系
      通过继承,子类可以获得父类的属性与方法,并且可以在此基础上进行扩展或修改。

    • 基本概念

      继承用于描述子类与父类之间的行为和属性关系。

      例如,现有"猫"、"狗"、"鸟"等类,它们都属于"动物"这个父类。因此,"动物"类可以定义这些子类共有的属性和行为,比如它们都拥有"年龄"属性,并且都能执行"进食"行为。

    • 想要实现继承需要使用关键字extends

      • 格式:

        访问修饰限定符 子类类名 extends 父类类名 { 
                // 成员属性
                // 成员行为
         }
    • 实现继承的几个注意事项:

    1. 如果父类有显示构造方法,其子类必须重写父类的显示构造方法

      class Animal {
          String name;
          int age;
          public Animal(String name,int age) {
              this.name = name;//父类构造方法
              this.age = age;
          }
      }
      
      class Dog extends Animal {
          public Dog(String name,int age) {
              super(name,age);//实现父类的构造方法 如不进行实现编译器会报错
          }
      }
    2. 如果父类为抽象类,且含有抽象方法,那么子类必须实现父类的抽象方法
       

      abstract class Animal {
          String name;
          int age;
      
          abstract public void eat();//父类的抽象方法
      }
      
      class Dog extends Animal {
      
      /*
          public void setName(String  name) {
              this.name = name;
          }
          public void setAge(int  age) {
              this.age = age;
          }
          public String getName() {
              return this.name;
          }
          public int getAge() {
              return this.age;
          }
      */
          public void eat() { // 子类实现 抽象方法
              System.out.println(this.name + " 正在吃狗粮");
          }
      
      }
    3. Java只支持单继承,如果想要实现多继承 需要通过接口进行实现

  • 方法重写(Override)与 方法重载(Overload)区别

    • 方法重写:

      • 发生方法重写的前提:

      1. 必须在继承体系中(父类和子类的关系)。

      2. 方法名相同

      3. 参数列表必须完全一致(包括顺序和类型)。

      4. 返回值类型必须相同或是父类返回值类型的子类型(协变返回类型)。

      5. 访问权限不能比父类更严格(可以更宽松)。

      6. 子类方法不能抛出比父类方法更多的检查型异常

    • 方法重写一般发生在多肽下

    • 方法重写的本质是动态绑定

      • 所谓动态绑定,就是在程序运行过程中,才知道调用哪一个方法,在编译期间是无法知晓的

      • 那么什么是动态绑定呢?

      • 动态绑定一般发生在多肽下

      • 假设现有一个父类Animal,其子类包含DogBird,且它们三者都定义了eat方法。
        当通过Dog对象调用父类的eat方法时,程序会将父类的eat方法与Dog类中的eat方法进行绑定,此时调用的就是Dog重写后的eat方法。
        同样,当通过Bird对象调用父类的eat方法时,父类的eat方法会与Bird类的eat方法绑定,此时调用的就是Bird重写后的eat方法。

        这就是所谓的动态绑定,它是在程序运行时确定调用哪个具体方法的机制,是多态的核心体现。

    • 方法重载与方法重写的区别

      区别方法重写方法重载
      发生位置发生在继承体系下发生在同一类中
      参数列表要求参数列表必须与父类一致参数列表需不同
      绑定方式发生动态绑定发生静态绑定
      返回类型限制返回类型必须与父类方法相同或是其子类型(协变返回类型)无限制
      访问权限限制子类方法访问权限不能比父类方法更严格(可放宽或保持一致)无限制
    • 一个实例:

    • class Animal {
          String name;
          public void eat() {
              System.out.println("吃");
          }
      
          public Animal(String name) {
              this.name = name;
          }
      }
      class Dog extends  Animal {
          public Dog(String name) {
              super(name);
          }
      
          public void eat() {
              System.out.println(this.name + " 正在吃狗粮");
          }
      }
      class Bird extends Animal {
          public Bird(String name) {
              super(name);
          }
      
          public void eat() {
              System.out.println(this.name + " 正在吃鸟粮");
          }
      }
      public class Test2 {
          public static void eatAnimals(Animal animal) {
              animal.eat();
          }
          public static void main(String[] args) {
              Dog dog = new Dog("小黄");
              Bird bird = new Bird("小翠");
              eatAnimals(dog);
              eatAnimals(bird);
          }
      }
      

      运行结果:
              小黄 正在吃狗粮
              小翠 正在吃鸟粮

    • 回看 main 方法,调用了 eatAnimals 方法,虽然该方法中都是通过 Animal 类型 来调用 eat 方法,但输出结果却不相同。这正体现了 方法重写动态绑定 的效果。

  • super 关键字

    • 用于将子类的参数传递给父类的构造方法或调用父类的方法。

  • 多态 

    • 概念:通俗来说,就是不同人去干同一件事情会发生不同的结果

    • 多态发生的前提:

      • 必须发生在继承体系

      • 父类与子类必须发生方法重写

      • 发生动态绑定

    • 一个实例:

      class Animal {
          String name;
          public void eat() {
              System.out.println("吃");
          }
      
          public Animal(String name) {
              this.name = name;
          }
      }
      class Dog extends  Animal {
          public Dog(String name) {
              super(name);
          }
      
          public void eat() {
              System.out.println(this.name + " 正在吃狗粮");
          }
      }
      class Bird extends Animal {
          public Bird(String name) {
              super(name);
          }
      
          public void eat() {
              System.out.println(this.name + " 正在吃鸟粮");
          }
      }
      public class Test2 {
          public static void eatAnimals(Animal animal) {
              animal.eat();
          }
          public static void main(String[] args) {
              Dog dog = new Dog("小黄");
              Bird bird = new Bird("小翠");
              eatAnimals(dog);
              eatAnimals(bird);
          }
      }
      
  • 运行结果:
            小黄 正在吃狗粮
            小翠 正在吃鸟粮

  • 回看 main 方法,调用了 eatAnimals 方法,虽然该方法中都是通过 Animal 类型 来调用 eat 方法,但输出结果却不相同。这正体现了 方法重写动态绑定 的效果。

 八、接口

  • 什么是接口(Interface)

    • 接口(Interface)是Java中一种特殊的抽象类型,用来定义一组方法的规范,但不包含具体实现。简单来说,接口就像一个“合同”,规定了某些方法的名字、参数和返回类型,但不写具体的代码细节,具体怎么做由实现这个接口的类来完成。

  • 接口和类的区别

    • 接口和类的区别
      方面接口
      定义描述对象的属性与行为定义一组方法规范
      关键字classinterface
      方法可以有具体实现只能声明抽象方法
      变量可以有实例变量只能有常量
      实例化可以创建对象不可以进行实例化
      继承只支持单继承支持多继承
      实现直接定义具体功能定义方法,由其他类进行实现
      目的实现具体功能提供接口、规范行为、定义标准
      多肽体现通过父类引用通过接口引用
    • 接口相当于加了一层检验,并规范化了许多方法的类

    • 简单来说:

      • 是具体的,能实现方法并创建对象;

      • 接口是抽象的,只定义规则,不能直接用,要由类去实现。

  • 接口的作用

    • 规范行为:定义一组方法,保证实现接口的类都必须提供这些方法,形成统一标准。

    • 实现多态:接口类型可以指向不同实现类的对象,方便代码扩展和维护。

    • 支持多继承:Java类只能单继承,但接口可以多继承,方便功能组合。

2. 接口的定义和语法

  • 如何定义接口(interface关键字)

    • public interface 接口名 {
          // 常量(public static final,可以省略修饰符)
          数据类型 常量名 = 值;
      
          // 抽象方法(public abstract,可以省略修饰符)
          返回类型 方法名(参数列表);
      }
      
  • 接口中方法的默认特性(默认为抽象方法,无方法体)

    public interface MyInterface {
        // 常量
        int CONSTANT = 100;
    
        // 抽象方法
        void method1();
        int method2(String param);
    }
    
    • 成员方法
      • 接口中的成员方法默认被public abstract进行修饰,默认为抽象方法,但可以通过static 关键字使其变为静态方法
    • 成员属性
      • 接口中的成员属性默认被public static final所修饰,且为常量

3. 实现接口(implements

  • 类如何实现接口

    • 使用implements关键字来声明一个类实现某个接口。

    • 类必须实现接口中所有的抽象方法,否则该类要声明为abstract

      • 如果后续有类继承了该类,那么后续的类必须实现还未实现的所有抽象方法

      • 若后续类继承该类,则必须实现所有未完成的抽象方法

  • 一个类可以实现多个接口

    • 其格式如下:

      class 类名 implements 接口1,接口2,接口3…… {
          // 成员方法 属性
      }
    • 通过接口,便可以实现多继承的效果

注意事项:

接口不可以被实例化。接口是Java中一种完全抽象的类,它只包含抽象方法(在Java 8之后也可以包含默认方法和静态方法)和常量。接口的主要作用是定义一组规范或契约,由实现类来具体实现这些方法。由于接口本身没有具体实现,所以不能直接使用new关键字创建实例。

以下代码尝试实例化接口,但会导致编译错误。

public interface Animal {
    void eat();
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal(); // 编译错误:Animal是抽象的,不能被实例化
    }
}

the end *

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值