Java 零基础入门学习(小白也能看懂!)二

简介: Java 零基础入门学习(小白也能看懂!)二

Java 零基础入门学习(小白也能看懂!)一https://developer.aliyun.com/article/1494847

3. 运算符

3.1 什么是运算符

计算机的最基本的用途之一就是执行数学运算,比如:

int a = 10;
 int b = 20;
 a + b;
 a < b;

上述 + 和 < 等就是运算符,即:对操作数进行操作时的符号,不同运算符操作的含义不同。


作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。Java中运算符可分为以下:算术运算符(+ - * /)、关系运算符(< > ==)、逻辑运算符、位运算符、移位运算符以及条件运算符等

3.2 算术运算符

  1. 基本四则运算:加减乘除求模(+-*/%
int a = 20;
int b = 10;
System.out.println(a + b);     // 30
System.out.println(a - b);     // 10
System.out.println(a * b);     // 200
System.out.println(a / b);     // 2
System.out.println(a % b);     // 0 --->模运算相当于数学中除法的余数


【注意】:

  • 都是二元运算符,使用时必须要有左右两个操作数
  • int / int 结果还是int类型,而且会向下取整
int a = 3;
 int b = 2;
// 在数学中应该是1.5  但是在Java中输出结果为1  会向下取整,即小数点之后全部舍弃掉了
System.out.println(a / b); // 1

// 如果要得到数学中的结果,可以使用如下方式
double d = a * 1.0 / b;
System.out.println(d);// 1.5


  • 做除法和取模时,右操作数不能为0

  • % 不仅可以对整型取模,也可以对double类型取模,但是没有意义,一般都是对整型取模的
System.out.println(11.5 % 2.0);// 1.5

两侧操作数类型不一致时,向类型大的提升

System.out.println(1 + 0.2);   // +的左侧是int,右侧是double,在加之前int被提升为double
// 1.2


结合赋值和运算符(+=-=*=/=%=

该种类型运算符操作完成后,会将操纵的结果赋值给左操作数.

int a = 1;
a += 2; // 相当于 a = a + 2
System.out.println(a);   // 输出3

a -= 1;// 相当于 a = a - 1
System.out.println(a);   // 输出2

a *= 3;// 相当于 a = a * 3
System.out.println(a);   // 输出6

a /= 3;// 相当于 a = a / 3
System.out.println(a);   // 输出2

a %= 3;                  // 相当于 a = a % 2
System.out.println(a);   // 输出2
  1. 【注意】:只有变量才能使用该运算符,常量不能使用。
  2. 自增/自减运算符(++--)++是一种自增的操作符,又分为前置++和后置++,–是一种自增的操作符,又分为前置–-和后置–-。
  1. 前置++
public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = ++a;// ++的操作数是a,是放在a的前面的,就是前置++
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 11 , b = 11
    }
}


计算口诀:先 +1,后使用

a原来是10,先 +1,后a变成了11,再使用赋值给b,b得到的也是11,所以计算后,a和b都是11,等价于这样的代码:

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        a += 1;
        int b = a;
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 11 , b = 10
    }
}
  1. 后置++
public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = a++;// ++的操作数是a,是放在a的后面的,就是后置++
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 11 , b = 10
    }
}


计算口诀:先使用,后 +1

a原来是10,先使用,把a赋值给b,b变成了10,后a+1变成了10,所以计算后,a=11,b=10,等价于这样的代码:

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = a;
        a += 1;
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 11 , b = 10
    }
}


  1. 前置--

和前置++同理,只是换成了-1

计算口诀:先 -1,后使用

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = --a;// --的操作数是a,是放在a的前面的,就是前置--
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 9 , b = 9
    }
}


  1. 后置--

和后置++同理,只是换成了-1

计算口诀:先使用,后-1

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = a--;// --的操作数是a,是放在a的后面的,就是后置--
        System.out.printf("a = %d , b = %d",a , b);
        // 运行结果:a = 9 , b = 10
    }
}


【注意】:

  • 如果混合使用,【前置++】先+1,然后使用变量+1之后的值,【后置++】先使用变量原来的值,表达式 结束时给变量+1
  • 只有变量才能使用自增/自减运算符,常量不能使用,因为常量不允许被修改

3.3 关系运算符

关系远算符有6个:(==!=><>=<=),其结果是trueflase

int a = 10;
int b = 20;
// 注意:在Java中 = 表示赋值,要与数学中的含义区分
//在Java中 == 表示相等
System.out.println(a == b);       // false
System.out.println(a != b);       // true
System.out.println(a < b);        // true
System.out.println(a > b);        // false
System.out.println(a <= b);       // true
System.out.println(a >= b);       // false


【注意】:当需要多次判断时,不能连着写,比如:3 < a < 5,Java程序与数学中是有区别的

3.4 逻辑运算符(重点)

逻辑远算符主要由3个:(&&||!),运算结果都是 boolean类型。

逻辑与 &&


语法规则:表达式1 && 表达式2,左右表达式必须是boolean类型的结果。两个表达式都为真,结果才是真,只要有一个是假,结果就是假。

表达式1 表达式2 结果
int a = 1;
int b = 2;

System.out.println(a == 1 && b == 2);   // 左为真 且 右为真 则结果为真
System.out.println(a == 1 && b > 100);  // 左为真 但 右为假 则结果为假
System.out.println(a > 100 && b == 2);  // 左为假 但 右为真 则结果为假
System.out.println(a > 100 && b > 100); // 左为假 且 右为假 则结果为假

逻辑或 ||

语法规则:表达式1 || 表达式2,左右表达式必须是boolean类型的结果。

两个表达式都为假,结果才是假,只要由一个是真,结果就是真。

表达式1 表达式2 结果
int a = 1;
int b = 2;

System.out.println(a == 1 || b == 2);   // 左为真 且 右为真 则结果为真
System.out.println(a == 1 || b > 100);  // 左为真 但 右为假 则结果也为真
System.out.println(a > 100 || b == 2);  // 左为假 但 右为真 则结果也为真
System.out.println(a > 100 || b > 100); // 左为假 且 右为假 则结果为假

逻辑非 !

语法规则:! 表达式

真变假,假变真。

表达式 结果
int a = 1;
System.out.println(!(a == 1));   // a == 1 为true,取个非就是false
System.out.println(!(a != 1));  // a != 1 为false,取个非就是true

短路求值


&& 和 || 遵守短路求值的规则.


System.out.println(10 > 20 && 10 / 0 == 0);             // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0);             // 打印 true


【注意】:


对于 && , 如果左侧表达式值为 false, 则表达式结果一定是 false, 无需计算右侧表达式


对于 ||, 如果左侧表达式值为 true, 则表达式结果一定是 true, 无需计算右侧表达式.


& 和 | 如果表达式结果为 boolean 时, 也表示逻辑运算. 但与 && || 相比, 它们不支持短路求值.

记忆口诀:

&&:全真为真,有假必假,遇假则停

||:全假为假,有真必真,遇真则停

!:真变假,假变真


3.5 位运算符

Java 中数据存储的最小单位是字节,而数据操作的最小单位是比特位. 字节是最小的存储单位,每个字节是由8个二进制比特位组成的,多个字节组合在一起可以表示各种不同的数据。


位运算符主要有四个:&|~^,除~是一元运算符外,其余都是二元运算符。

位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位 的每一位依次进行计算。


按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0

int a = 10;
int b = 20;
System.out.println(a & b);
// 0000 1010  10的二进制
// 0001 0100  20的二进制
// 0000 0000

按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1

int a = 10;
int b = 20;
System.out.println(a | b);
// 0000 1010
// 0001 0100
// 0001 1110


.按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0

int a = 0xf;
System.out.printf("%x\n", ~a);


【注意】:

  • 0x 前缀的数字为 十六进制 数字. 十六进制可以看成是二进制的简化表示方式. 一个十六进制数字对应 4 个二进制位.
  • 0xf 表示 10 进制的 15, 也就是二进制的 1111
  • printf 能够格式化输出内容, %x 表示按照十六进制输出.
  • \n 表示换行符

按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1

int a = 0x1;
int b = 0x2;
System.out.printf("%x\n", a ^ b);
  1. 【注意】:如果两个数相同,则异或的结果为0

3.6 移位运算符(了解)

移位运算符有三个:<<>>>>>,都是二元运算符,且都是按照二进制比特位来运算的。

  1. 左移 <<: 最左侧位不要了, 最右侧补 0
int a = 0x10;
System.out.printf("%x\n", a << 1); 
// 运行结果(注意, 是按十六进制打印的)
//20
  1. 【注意】:向左移位时,丢弃的是符号位,因此正数左移可能会编程负数
  2. 右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
int a = 0x10;
System.out.printf("%x\n", a >> 1);
// 运行结果(注意, 是按十六进制打印的)
//8

int b = 0xffff0000;
System.out.printf("%x\n", b >> 1);
// 运行结果(注意, 是按十六进制打印的)
//ffff8000

无符号右移 >>>: 最右侧位不要了, 最左侧补 0.

int a = 0xffffffff;
System.out.printf("%x\n", a >>> 1);
// 运行结果(注意, 是按十六进制打印的)
//7ffffff

【注意】:


左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方.

右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方.

由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替.

移动负数位或者移位位数过大都没有意义.

3.7 条件运算符

条件运算符只有一个: 表达式1 ? 表达式2 : 表达式3

当 表达式1 的值为true时, 整个表达式的值为 表达式2 的值;

当 表达式1 的值为 false 时, 整个表达式的值为 表达式3 的值.

也是 Java 中唯一的一个 三目运算符, 是条件判断语句的简化写法.

// 求两个整数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;


【注意】:

  1. 表达式2和表达式3的结果要是同类型的,除非能发生类型隐式类型转换
int a = 10;
int b = 20;
int c = a > b? 1 : 2.0;


  1. 表达式不能单独存在,其产生的结果必须要被使用。

3.8 运算符的优先级


在一条表达式中,各个运算符可以混合起来进行运算,但是运算符的优先级不同,比如:* / 的优先级要高于 +-,有些情况下稍不注意,可能就会造成很大的麻烦。

// 求a和b的平均值
int a = 10;
int b = 20;
int c = a + (b - a) >> 1;
System.out.println(c);


上述表达式中,由于 + 的优先级要高于 >> , 因此a先和b-a的结果做加法,整体为20,最后再进行右移,因此结果 为10。

【注意】:运算符之间是有优先级的. 具体的规则我们不必记忆. 在可能存在歧义的代码中加上括号即可.

4. 程序逻辑控制

4.1 顺序结构

顺序结构比较简单,按照代码书写的顺序一行一行执行。

public class Test {
    public static void main(String[] args) {
        System.out.println("aaa");
        System.out.println("bbb");
        System.out.println("ccc");
        /* 运行结果
            aaa
            bbb
            ccc
         */
    }
}

如果调整代码的书写顺序, 则执行顺序也发生变化。

public class Test {
    public static void main(String[] args) {
        System.out.println("aaa");
        System.out.println("ccc");
        System.out.println("bbb");
        /* 运行结果
            aaa
            ccc
            bbb
         */
    }
}

4.2 分支结构(选择结构)

它的作用是根据判断的条件是否成立(真或假),来决定后续代码执行顺序。

举例:

如同在岔路口做选择。不同的选择会带来不同的路径及结果。


4.2.1 if 语句

  1. 语法格式1
if (布尔表达式) {
    // 语句
}


如果布尔表达式结果为true,执行if中的语句,否则不执行。

执行流程:

举例:小明,如果这次考试考60分或以上就不挂科。

public class Test {
    public static void main(String[] args) {
        int score = 90;
        if (score >= 60) {
            System.out.println("不挂科");
        }
    }
}

语法格式2

if (布尔表达式) {
    // 语句
} else {
    // 语句
}

如果布尔表达式结果为true,执行if中的语句,执行else中的语句。

执行流程:

举例:小明,如果这次考试考60分或以上就不挂科,没有考到60分就挂科。

public class Test {
    public static void main(String[] args) {
        int score = 90;
        if (score >= 60) {
            System.out.println("不挂科");
        } else {
            System.out.println("挂科");
        }
    }
}

语法格式3

if(布尔表达式1){
    // 语句1
 }else if(布尔表达式2){
    // 语句2
 }else{
    // 语句2
}

表达式1成立,执行语句1,否则表达式2成立,执行语句2,否则执行语句3


比如:考虑到学生自尊,不公开分数排名,因此:


分数在 [90, 100] 之间的,为优秀

分数在 [80, 90) 之前的,为良好 分数在 [70, 80) 之间的,为中等

分数在 [60, 70) 之间的,为及格 分数在 [ 0, 60) 之间的,为不及格

错误数据按照上述办法通知学生成绩。

public class Test {
    public static void main(String[] args) {
        int score = 90;
        if(score >= 90){
            System.out.println("优秀");
        }else if(score >= 80 && score < 90){
            System.out.println("良好");
        }else if(score >= 70 && score < 80){
            System.out.println("中等");
        }else if(score >= 60 && score < 70){
            System.out.println("及格");
        }else if(score >= 0 && score < 60){
            System.out.println("不及格");
        }else{
            System.out.println("错误数据");
        }
    }
}


【注意事项】:

  • 代码风格
 // 风格1-----> 推荐
int x = 10;
 if (x == 10) {
    // 语句1
 } else {
    // 语句2
 }
 
// 风格2
int x = 10;
 if (x == 10)
 {
    // 语句1
 }
 else
 {
    // 语句2
 }


  • 虽然两种方式都是合法的, 但是 Java 中更推荐使用风格1,代码跟紧凑。
  • 分号问题
int x = 20;
if (x == 10); 
{
    System.out.println("hehe");
}
 
// 运行结果
hehe
  • 此处多写了一个 分号, 导致分号成为了 if 语句的语句体, 而 { } 中的代码已经成为了和一个 if 无关的代码块,所以运行结果是haha,而不是空白。
  • 悬垂 else 问题
int x = 10;
int y = 10;
 if (x == 10) 
    if (y == 10)
        System.out.println("aaa");
else 
    System.out.println("bbb");


  • if else 语句中可以不加大括号 . 但是也可以写语句(只能写一条语句). 此时 else 是和最接近的 if 匹配. 但是实际开发中我们不建议这么写. 最好加上大括号.

4.2.2 switch 语句

基本语句

switch(表达式){
    case 常量值1:{
        语句1;
        [break;]
        }
    case 常量值2:{
        语句2;
        [break;]
        }
    ...
    default:{
        内容都不满足时执行语句;
        [break;]
        }       
 }

执行流程:

  1. 先计算表达式的值
  2. case依次比较,一旦有响应的匹配就执行该项下的语句,直到遇到break时结束
  3. 当表达式的值没有与所列项匹配时,执行default

代码示例:

public class Test {
    public static void main(String[] args) {
        String week = "周四";
        switch (week) {
            case "周一":
                System.out.println("埋头苦干,写程序");
                break;
            case "周二":
                System.out.println("请求学长帮忙解决bug");
                break;
            case "周三":
                System.out.println("今晚烧烤、小龙虾");
                break;
            case "周四":
                System.out.println("帮助学妹解决bug");
                break;
            case "周五":
                System.out.println("今晚吃鸡");
                break;
            case "周六":
                System.out.println("上GitHub交友");
                break;
            case "周日":
                System.out.println("郁郁寡欢、准备上课");
            default:
                System.out.println("输入错误");
        }
    }
}


【注意事项】:

  • 多个case后的常量值不可以重复
  • switch的括号内只能是以下类型的表达式:
  • 基本类型:byte、char、short、int,注意不能是long类型
  • 引用类型:String常量串、枚举类型
  • break 不要遗漏, 否则会失去 “多分支选择” 的效果
int day = 1;
switch(day) {
    case 1:
        System.out.println("星期一");
        // break;
    case 2:
        System.out.println("星期二");
        break;
 }
// 运行结果
星期一
星期二

switch 不能表达复杂的条件

// 例如: 如果 num 的值在 10 到 20 之间, 就打印 hehe
 // 这样的代码使用 if 很容易表达, 但是使用 switch 就无法表示. 
if (num > 10 && num < 20) {
    System.out.println("hehe");
}

switch 虽然支持嵌套, 但是很丑,一般不推荐

public class Test {
    public static void main(String[] args) {
        int x = 1;
        int y = 1;
        switch(x) {
            case 1:
                switch(y) {
                    case 1:
                        System.out.println("hehe");
                        break;
                }
                break;
            case 2:
                System.out.println("haha");
                break;
        }
    }
}

综上, 我们发现, switch的使用局限性是比较大的

4.3 循环结构

循环结构它是在满足条件的情况下,反复执行某一段代码的计算过程。

举例:

我们要围着操场跑 5 圈,跑圈这个行为就重复了 5 次,也就是循环了 5 次。


4.3.1 while 语句

基本格式:

while (循环条件) {
    // 语句
}

执行流程:

首先执行判断表达式,表达式的值为false,循环直接结束;表达式的值为true,则执行循环语句,语句执行完后再继续判断,是否进行下一次判断。

代码示例:打印1~10的值

public class Test {
    public static void main(String[] args) {
        int i = 1;
        while (i <= 10) {
            System.out.println(i);
            i++;
        }
    }
}

4.3.2 for 语句

基本格式:

for (表达式1;表达式2;表达式3) {
    // 语句
}


表达式1:用于循环变量的初始化

表达式2:用于循环结束条件的判断

表达式3:用于循环变量的调整


执行流程:


首先执行表达式1初始化循环变量,接下来就是执行表达式2的判断部分,表达式2的结果如果为false,则循环结束;如果表达式2的结果为true,则执行循环语句,循环语句执行完后,再去执行表达式3,调整循环变量,然后再去表达式2的地方执行判断,表达式2的结果是否为false,决定循环是否继续。

整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环

代码示例:打印1~10的值

public class Test {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
        }
    }
}


基本格式:

do{
    语句
}while(表达式);


while 和 for 这两种循环都是先判断,条件如果满足就进入循环,执行循环语句,如果不满足就跳出循环;

而do...while 循环则是先直接进入循环体,执行循环内部,然后在执行 while 后的判断表达式,表达式为真,就会进行下一次,表达式为false,则不再继续循环。


执行流程:


在do...while 循环中先执行图上的“语句”,执行完语句,在去执行“判断表达式”,在判断表达式的结果是true,则继续循环,执行循环;判断表达式的结果false,则循环结束。

所以在do...while 语句中循环体是至少执行一次的,这是do...while 循环比较特殊的地方。

代码示例:打印1~10的值

public class Test {
    public static void main(String[] args) {
        int i = 1;
        do {
            System.out.println(i);
            i++;
        } while (i <= 10);
    }
}


4.3.4 while语句 和 for语句的对比

4.4 break和continue


在循环执行的过程中,如果某些状况发生的时候,需要提前终止循环。

  • break 的作用是用于永久的终止循环,只要break 被执行,直接就会跳出循环,继续往后执行。
    代码示例:找到100~200之间的第一个3的倍数
public class Test {
    public static void main(String[] args) {
        for (int num = 100; num <= 200; num++) {
            if (num % 3 == 0) {
                System.out.println(num);
                break;
            }
        }
    }
}
// 运行结果 102


continue 的作用是跳出本次循环 continue 后边的代码

代码示例:找到100~200中的所有3的倍数

public class Test {
    public static void main(String[] args) {
        for (int num = 100; num <= 200; num++) {
            if (num % 3 == 0){
                System.out.println(num);
                continue;
            }
        }
    }
}

5. 方法

5.0 前言

在编程的过程中,经常会出现一部分代码多次使用的情况,比如计算多边形面积,输出固定格式的文字等。

今天我们就来学习方法。


5.1 方法的概念和使用

5.1.1 什么是方法

方法就是一个代码片段,类似于C语言的“函数”。

方法存在的意义:


  • 是能够模块化的组织代码(当代码规模比较复杂的时候)
  • 做到代码被重复使用, 一份代码可以在多个位置使用.
  • 让代码更好理解更简单.
  • 直接调用现有方法开发, 不必重复造轮子


5.1.2 方法的定义

方法的语法格式:

修饰符 返回值类型 方法名称(形参列表){
    // 方法体
    return 返回值;
}

代码示例:实现一个两个整数相加的方法

public static int add(int a, int b) {
    int c = a + b;
    return c;
}


【注意事项】:


修饰符:现阶段直接使用public static 固定搭配

返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成

void

方法名字:采用小驼峰命名

参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开

方法体:方法内部要执行的语句

在 Java当中,方法必须写在类当中

在 Java当中,方法不能嵌套定义

在 Java当中,没有方法声明一说

5.1.3 方法调用的过程

方法调用过程:

调用方法—>传递参数—>找到方法地址—>执行被调方法的方法体—>被调方法结束返回—>回到主调方法继续往下执行


【注意事项】

  • 定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
  • 一个方法可以被多次调用

代码示例:计算两个整数相加

public class Main {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int ret = add(x, y);
        System.out.println(ret);
    }

    public static int add(int a, int b) {
        int c = a + b;
        return c;
    }
}

方法调用过程:

  • DeBug

  • 内存图
    方法是放在方法区中的,被调用的时候,需要进入到栈内存中运行



  • 一旦程序遇到return或者方法执行结束,就会把当前方法栈帧就从栈上进行销毁(回收)

5.1.4 形参和实参(重要)


方法的形参相当于数学函数中的自变量,比如:1 + 2 + 3 + … + n的公式为sum(n) =(1 + n) * n / 2


Java中方法的形参就相当于sum函数中的自变量n,用来接收sum函数在调用时传递的值的。形参的名字可以随意取,对方法都没有任何影响,形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。

public class Main {
    public static void main(String[] args) {
        getSum(10); // 10是实参,在方法调用时,形参n用来保存10
        getSum(100); // 100是实参,在方法调用时,形参n用来保存100
    }

    private static int getSum(int n) { // n 是形参
        return (1 + n) * n / 2;
    }
}

再比如:

public class Main {
    public static void main(String[] args) {
        add(2,3); // 2 和 3 是实参,在调用时传给形参a 和 b
    }
    public static int add(int a, int b) {
        return a + b;
    }
}


5.1.5 没有返回值的方法

方法的返回值是可选的. 有些时候可以没有的,没有时返回值类型必须写成void

代码示例:

public class Main {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        print(a,b);
    }

    private static void print(int x, int y) {
        System.out.println("x = " + x + ",y = " + y);
    }
}


return问题

  • 如果没有返回值的方法要写return,则return后面不能加任何返回值。

  • return后面的语句不会被执行,return表示方法的结束

5.2 方法的重载

5.2.1 为什么需要方法的重载



由于参数类型不匹配, 所以不能直接使用现有的 add方法.

一种比较简单粗暴的解决方法如下:

public class Main {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        System.out.println(addInt(x,y));

        double a = 1.1;
        double b = 1.2;
        System.out.println(addDouble(a,b));
    }
    public static int addInt(int a, int b) {
        return a + b;
    }
    public static double addDouble(double a, double b) {
        return a + b;
    }
}


上述代码确实可以解决问题,但不友好的地方是:需要提供许多不同的方法名,而取名字本来就是让人头疼的事情。那能否将所有的名字都给成 add 呢?


5.2.2 方法重载的概念

在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。

public class Main {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        System.out.println(add(x,y));// 调用add(int, int)

        double a = 1.1;
        double b = 1.2;
        System.out.println(add(a,b));// 调用add(double, double)
        
        double c = 1.3;
        System.out.println(add(a,b,c));// 调用add(double, double,double)
    }
    
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static double add(double a, double b) {
        return a + b;
    }
    
    public static double add(double a, double b, double c) {
        return a + c;
    }
}


注意:

  1. 方法名必须相同
  2. 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
  3. 与返回值类型是否相同无关


5.2.3 方法签名

在同一个作用域中不能定义两个相同名称的标识符。比如:方法中不能定义两个名字一样的变量,那为什么类中就可以定义方法名相同的方法呢?

方法签名即:经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方法完整的名字。


5.3 递归

5.3.1 生活中的例子

从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:

"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:

“从前有座山,山上有座庙…”

“从前…”


上面的两个例子有个共同的特征:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了


5.3.2 递归的概念


一个方法在执行过程中调用自身, 就称为 “递归”.

递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.


例如, 我们求 N!

起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.

递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!


递归的必要条件:


将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同

递归出口代码示例

public class Main {
    public static void main(String[] args) {
        fun();
    }
    
    public static void fun() {
        fun();
    }
}

上述代码就是一个最简单的递归。

但是存在错误:出来栈溢出错误的时候,就说明结束条件不对或者没有结束条件

代码示例:递归求 N 的阶乘

public class Main {
    public static void main(String[] args) {
        System.out.println(factor(5)); // 120
    }

    public static int factor(int n) {
        if (n == 1) {
            return 1;
        }
        return factor(n - 1) * n; // factor()方法调用自己
    }
}

5.3.3 递归执行过程分析

递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 "方法执行结束之后, 回到调用位置继续往下执行.


代码示例:递归求 N 的阶乘

public class Main {
    public static void main(String[] args) {
        System.out.println(factor(5));
    }

    public static int factor(int n) {
        System.out.println("函数开始, n = " + n);
        if (n == 1) {
            System.out.println("函数结束, n = 1 ret = 1");
            return 1;
        }
        int ret = n * factor(n - 1);
        System.out.println("函数结束, n = " + n + " ret = " + ret);
        return ret;
    }
}
/*
函数开始, n = 5
函数开始, n = 4
函数开始, n = 3
函数开始, n = 2
函数开始, n = 1
函数结束, n = 1 ret = 1
函数结束, n = 2 ret = 2
函数结束, n = 3 ret = 6
函数结束, n = 4 ret = 24
函数结束, n = 5 ret = 120
120
*/


执行图:

6. 数组

6.1 数组的基本概念


6.1.1 为什么使用数组?

假设现在要存储5个学生的年龄,按照之前掌握的知识点,我们会写出如下代码:声明5个变量存储学生变量

pubpublic class Test {
    public static void main(String[] args) {
        int age1;
        int age2;
        int age3;
        int age4;
        int age5;
    }
}


如果我们有10个学生呢?我们就要声明20个变量,似乎没有什么问题。那如果有100,1000个学生呢,我们就要声明100,1000个变量,这样就有点离谱了,使用数组我们就可以解决一个问题。


6.1.2 什么是数组

数组,是指一组类型相同的数据的集合,数组中每个数据称为元素。数组可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致。数组分为一维数组和多维数组。

数组在内存中是一段连续的空间,比如现实中的车库:


在 Java中,包含6个整形类型元素的数组,就相当于上图中连在一起的6个车位,从上图中可以看到:

  1. 数组中存放的元素其类型相同
  2. 数组的空间是连在一起的
  3. 每个空间有自己的编号,起始位置的编号为0,即数组的下标。


6.1.3 数组的创建和初始化

6.1.3.1 数组的创建

基本语法格式:

T[] 数组名 = new T[N];
  • T:表示数组中存放元素的类型
  • T[]:表示数组类型
  • N:表示数组的长度

代码示例:存储10个人的年龄

int[] ages = new int[10];


6.1.3.2 数组的初始化

Java 数组初始化主要分为静态初始化以及动态初始化

  1. 动态初始化:在创建数组时,直接指定数组中元素的个数
int[] ages = new int[10];


动态初始化:在创建数组是不直接指定数据元素个数,而直接讲具体的数据内容进行指定

语法格式:

T[] 数组名 = {data1,data2,....data};
int[] ages = new {1,2,3,4,5};

【注意事项】

  • 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
  • 静态初始化时, {}中数据类型必须与[]前数据类型一致。
  • 静态初始化可以简写,省去后面的new T[]。
int[] arr = {1,3,2,5,4};
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原


数组也可以按照如下C语言个数创建,不推荐

int arr[] = {1, 2, 3};
/*
该种定义方式不太友好,容易造成数组的类型就是int的误解
[]如果在类型之后,就表示数组类型,因此int[]结合在一块写意思更清晰
*/


静态和动态初始化也可以分为两步,但是省略格式不可以。

public class Main {
    public static void main(String[] args) {
        int[] array1;
        array1 = new int[10];
        
        int[] array2;
        array2 = new int[]{10, 20, 30};
        
        // 注意省略格式不可以拆分, 否则编译失败
        //int[] array3;
        //array3 = {1, 2, 3};
    }
}

  • 如果没有对数组进行初始化,数组中元素有其默认值
  • 如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值,比如:
类型 默认值
byte 0
short 0
int 0
long 0
float 0.0f
double 0.0
char /u0000
boolean false


  • 如果数组中存储元素类型为引用类型,默认值为null

6.1.4 数组的使用

6.1.4.1 数组中元素访问

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。比如:

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};

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


【注意事项】:

  1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标快速访问数组中任意位置的元素
  2. 下标从0开始,介于[0,N) 之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。


6.1.4.2 遍历数组

所谓 “遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印。

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};

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

上述代码可以起到对数组中元素遍历的目的,但问题是:


如果数组中增加了一个元素,就需要增加一条打印语句


如果输入中有100个元素,就需要写100个打印语句


如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。


通过观察代码可以发现,对数组中每个元素的操作都是相同的,则可以使用循环来进行打印。

1. 循环遍历数组

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};
        for (int i = 0; i < 5; i++) {
            System.out.println(arr[i]);
        }
    }
}


改成循环之后,上述三个缺陷可以全部2和3问题可以全部解决,但是无法解决问题1。那能否获取到数组的长度呢?

【注意】:在数组中可以通过 数组对象.length 来获取数组的长度

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

2. 使用 for-each 遍历数组

语法格式:

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};
        for (int x : arr) {
            System.out.println(x);
        }
    }
}

for-each for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.

for-each循环语句的循环变量将会遍历数组中的每个元素,而不是下标值。

3. 数组转字符串输出

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};
        String ret = Arrays.toString(arr);
        System.out.println(ret);
    }
}

代码分析:

6.2 数组是引用类型

6.2.1 JVM 内存分布

内存是一段连续的存储空间,主要是用来存储程序运行时数据的。比如:


程序运行时代码需要加载到内存

程序运行产生的中间数据要存放在内存

程序中的常量也要保存

有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁。

如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:

因此 JVM 也对所使用的内存按照功能的不同进行了划分:



程序计数器:只是一个很小的空间,保存下一条执行的指令的地址

虚拟机栈:与方法调用相关的一些信息,每个方法在执行时,都会先创建栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后吧,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。

本地方法栈:本地方法栈于虚拟机栈的作用类似,只不过保存的内容是方法的局部变量。在有些版本的 JVM 实现中,本地方法栈和虚拟机栈是一起的

堆:JVM 所管理的最大内存区域,使用**new创建的对象都是在堆上保存,堆是随着程序开始运行时而创建,随着程序的结束而销毁,堆中的数据只要还有在使用,就不会被销毁**

方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法编译出的字节码就是保存在这个区域。


6.2.2 基本类型的变量与引用类型变量的区别

基本数据类型的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;

而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址

public class Main {
    public static void main(String[] args) {
        int a = 10;
        int[] arr = new int[]{1,2,3};
    }
}


在上述代码中,aarr,都是函数内部的变量,因此其空间都在main方法对应的栈帧中分配。

a是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。

arr是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。



上图可以看出,引用变量并不直接存储对象本生,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。有点类似C语言中的指针,但是 Java 中引用要比指针的操作更简单。


Java 零基础入门学习(小白也能看懂!)三https://developer.aliyun.com/article/1494897

相关文章
|
1天前
|
缓存 Java 数据库
Java并发编程学习11-任务执行演示
【5月更文挑战第4天】本篇将结合任务执行和 Executor 框架的基础知识,演示一些不同版本的任务执行Demo,并且每个版本都实现了不同程度的并发性。
18 4
Java并发编程学习11-任务执行演示
|
2天前
|
数据库连接
java+ssm+vue代码视频学习讲解
java+ssm+vue代码视频学习讲解
5 0
|
11天前
|
算法 Java 大数据
Java从入门到精通学习报告
Java从入门到精通学习报告
19 1
|
11天前
|
消息中间件 监控 安全
【JAVAEE学习】探究Java中多线程的使用和重点及考点
【JAVAEE学习】探究Java中多线程的使用和重点及考点
|
12天前
|
Java
【专栏】Java 8 的 Streams 提供了一种处理数据集合的新方式,增强了代码的可读性和可维护性
【4月更文挑战第28天】Java 8 的 Streams 提供了一种处理数据集合的新方式,增强了代码的可读性和可维护性。本文介绍了 Streams 的基本概念,如从数据源创建 Stream,以及中间和终端操作。通过过滤、映射、归并、排序、分组等案例,展示了 Streams 的使用,包括并行 Streams 提高效率。学习 Streams 可以提升代码质量和效率,文章鼓励读者在实际开发中探索更多 Streams 功能。
|
13天前
|
存储 监控 Java
Java 零基础入门学习(小白也能看懂!)四
Java 零基础入门学习(小白也能看懂!)四
|
13天前
|
存储 安全 Java
Java 零基础入门学习(小白也能看懂!)三
Java 零基础入门学习(小白也能看懂!)三
|
13天前
|
Java 编译器 程序员
Java 零基础入门学习(小白也能看懂!)一
Java 零基础入门学习(小白也能看懂!)一
|
13天前
|
Java
Java学习——类与对象(二)
Java学习——类与对象(二)
|
13天前
|
Java C++
Java学习——类与对象(一)
Java学习——类与对象(一)