六、表达式

表达式(expression),是由数字、运算符、括号、常量、变量等以能求得结果的组合

在这里插入图片描述

七、数据类型的转换

在8大基本数据类型中,boolean不属于数值类型,所以不参与转换,其他类型的转换规则如下图。一般的,byte、short、char三种类型相互之间一般不参与转换操作,按照转换方式,有两种(注意:boolean类型不参与类型转换):

  1. 自动类型转换:范围小的数据类型直接转换成范围大的数据类型,小->大
  2. 强制类型转换:范围大的数据类型强制转换成范围小的数据类型,大->小
    在这里插入图片描述

7.1、自动类型转换与提升

7.1.1、自动类型转换

自动类型转换,也称为“隐式类型转换,就是把范围小的数据类型直接转换成范围大的数据类型

转换规则:byte、short、char—>int—>long—>float—>double

注意事项:byte、short、char相互之间不转换,他们参与运算首先转换为int类型

语法格式:范围大的数据类型 变量 = 范围小的数据类型值

public class TypeConvertDemo1{
    public static void main(String[] args) {
        //把int类型变量转为long类型
        long longNumber = 17;//整数默认为int类型
        //把long类型转换为float类型
        float f1 = longNumber;
        //把float类型转换为double类型
        double d = f1;

        //定义两个int类型的变量
        int a1 = 2;
        int b1 = 3;
        int c1 = a1 + b1;

        //定义一个byte类型,一个int类型
        byte b2 = 2;
        int c2 = 3;
        System.out.println(b2 + c2);

        //byte、short、char类型参与运算时把自己提升为int类型
        //byte d1 = b2 + c2;//编译报错
        int d3 = b2 + c2;//编译通过
    }
}

7.1.2、自动类型提升

当一个算术表达式中,包含多个基本数据类型的常量或变量(boolean除外)时,整个算术表达式的结果类型将出现自动提升,其规则是:

  • 所有的byte、short、char类型被自动提升到int类型,再参与运算

  • 整个表达式的最终结果类型,被提升到表达式中类型最高的类型
System.out.println('a' + 1);//98
byte b = 22;
b = b + 11;//编译出错,此时结果类型应该是int
double d1 = 123 + 1.1F + 3.14 + 99L ;

结论:算数表达式结果的类型就是其中范围最大的数据类型。

7.2、强制类型转换

强制类型转换,也称为“显式类型转换”,就是把范围大的数据类型强制转换成范围小的数据类型

# 语法格式:
# 范围小的数据类型  变量 = (范围小的数据类型)范围大的数据类型值;

注意:一般情况下不建议使用强转,因为强转有可能损失精度

public class TypeConvertDemo2{
    public static void main(String[] args) {
        int a = 2;
        byte b = 3;
        //自动类型转换
        int c = a + b;
        //强制类型转换
        byte d = (byte) (a + b);
        //把double转换为int类型
        int i = (int)3.14;
        System.out.println(i);//3
    }
}

八、运算符

对常量和变量进行操作的符号称为运算符

常见的运算符分为:算术运算符、赋值运算符、比较运算符、逻辑运算符、三元运算符

8.1、算数运算符

运算符 运算规则1 示例 结果
+ 正号 +3 3
+ 52+3 5
+ 拼接字符串 "中"+"国" “中国”
- 符号 int a = 3<br />-a -3
- 3-1 2
* 2*3 6
/ 5/2 2
% 取模 5%2 1
++ 自增 int a = 1<br/>a++(++a) 2
-- 自减 int b =2 <br/>b--(--b) 1

8.2、自增和自减

自增:++,递增操作符,使变量值增加1,有前置和后置之分,只能操作变量

自减:-- ,递减操作符,使变量值减去1,有前置和后置之分,只能操作变量

以++为例:

a++和++a结果都是让a的值自增1,如果只是需要自增的话,使用哪一种都可以

但是他们俩有唯一的区别:

  1. 前置(++a): 表示对a加1之后的结果进行运算,先加后用
  2. 后置(a++):表示对a变量加1之前的值(原始值)进行运算,先用后加
public class ArithmeticOperatorsDemo2{
    public static void main(String[] args) {
        int a1 = 5;
        int b1 = ++ a1;
        System.out.println("a1=" + a1 + ",b1=" + b1);//a1=6,b1=6
        int a2 = 5;
        int b2 = a2 ++;
        System.out.println("a2=" + a2 + ",b2=" + b2);//a2=6,b2=5
    }
}

比较底层的解释

++a表示取a的地址,增加它的内容,然后把值放在寄存器中
a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值

8.3、赋值运算符

运算符 运算规则 示例 结果
== 是否相等于 4==3 false
!= 是否不等于 !4=3 true
< 小于 4<3 false
> 大于 4>3 true
<= 小于等于 4<=3 false
>= 大于等于 4>=3 true

8.4、三元运算符

三元运算符,表示有三个元素参与的表达式,所以又称为三目运算符,其语义表示if-else(如果什么情况就做什么,否则做什么)

语法格式:数据类型 变量 = boolean表达式 ? 结果A :结果B

表达的意思,boolean表达式结果:

  • 为true,则三元运算符的结果是结果A
  • 为false,则三元运算符的结果是结果B

注意:

  1. 三元运算符必须定义变量接受运算的结果,否则报错
  2. 三元运算符结果的类型由结果A和结果B来决定的,结果A和结果B的类型是相同的

8.5、逻辑运算符

逻辑运算符用于连接两个boolean表达式,结果也是boolean类型的

运算符 运算规则 示范 结果
& false & true false
| false | true true
^ 异或 true ^ false true
! !true false
&& 短路与 false && false false
|| 短路或 false || false true

规律:

  • 非:取反,!true则false,!false则true

  • 与:有false则false

  • 或:有true则true

  • 异或:^ 相同则false,不同则true

8.5.1、&和&&( | 和 | |)的区别

& :&左边表达式无论真假,&右边表达式都进行运算

&& :如果&&左边表达式为真,&&右边表达式参与运算,否则&&右边表达式不参与运算,故称短路与

| 和 || 的区别同理,||,左边为真,右边不参与运算

public class LogicalOperatorDemo2 {
    public static void main(String[] args) {
        System.out.println(false & 1 / 0 == 1);//报错,说明右边执行了
        System.out.println(false && 1 / 0 == 1);//不报错,说明右边没有执行
        System.out.println(true | 1 / 0 == 1);//报错,说明右边执行了
        System.out.println(true | 1 / 0 == 1);//不报错,说明右边没有执行
    }
}

九、数组

9.1、JVM初探究

在这里插入图片描述

  • 程序计数器:当前线程所执行的字节码的行号的指示器
  • 本地方法栈:为虚拟机使用的native方法服务
  • 方法区:线程共享的内存区域,存储已经被虚拟机加载的类的信息、常量、静态变量,这个区域的内存回收的目标主要是针对常量池的回收和对类型的卸载
  • Java虚拟机栈:简称栈,每个方法被执行的同时会创建一个栈帧用于存储该方法的局部变量、操作栈、动态链接、方法出口等信息
  • Java堆:被所有线程共享的一块内存区域,在虚拟机创建的时候启动,所有对象的实例(new出来的对象)以及数组都要在堆上分配内存空间,所以堆占的内存空间远比栈大
  • 每当调用一个方法时,创建一个栈帧,存放了当前方法的局部变量,当方法调用完毕时,该方法的栈帧就被销毁了
  • 每次new一个对象的时候,就表示在内存中开辟了一块新的存储空间

9.2、数组

9.2.1、什么是数组

具有相同类型多个常量值有序组织起来的一种数据形式,数组中使用索引来表示元素存放的位置,索引从0开始步长是1,有点像Excel表格的行号

在这里插入图片描述

9.2.2、定义语法

数组元素类型[]  数组名;
int [] nums;

注意:

  1. 可以把int[] 看成一种数据类型,int类型的数组类型
  2. int[]数组表示,这个数组中的元素都是int类型的,同理

9.2.3、数组的初始化

数组在定义后,必须初始化才能使用。所谓初始化,就是在堆内存中给数组分配存储空间,并为每一个
元素赋上初始值,有两种方式:静态初始化和动态初始化数组的长度是固定的,无论以哪种,一旦初始化完成,数组的长度(元素的个数)就固定了,不能改变,除非重新对该初始化,初始化时,如果我们明确了具体的元素就用静态初始化,如果还不知道具体放哪些元素,只知道个数,用动态初始化

9.2.3.1、静态初始化

我们直接为每一个数组元素设置初始化值,而数组的长度由系统(JVM)决定

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

int[] nums = new  int[]{1,3,5,7,9};
//简单写法:
int[] nums = {1,3,5,7,9};//简单写法,定义和初始化必须同时写出来

9.2.3.2、静态初始化内存分析

public class ArrayDemo1{
  public static void main(String[] args) {
//定义并初始化数组
    int[] nums = new int[] { 1, 3, 5, 7 };
    System.out.println("数组长度=" + nums.length);
//重新初始化数组
    nums = new int[] { 2, 4, 8 };
    System.out.println("数组长度=" + nums.length);
  }
}

在这里插入图片描述

若 num = null,则null表示不再引用堆中的内存空间,那么此时nums就好比是没有初始化的,不能使用

9.2.3.4、动态初始化

程序员只设置数组元素个数,而数组的元素的初始值由系统(JVM)决定

语法:数组元素类型[] 数组名 = new 数组元素类型[length];
int[] nums = new int[5];

注意:不能同时指定元素值和数组长度,int[] nums = new int[5]{1,3,5,7,9}是错误的

内存图与静态初始化一样,只是拥有默认值

9.2.4、数组中对元素的操作

9.2.4.1、获取元素的个数

int size = 数组名.length;

9.2.4.2、设置元素

nums[1] = 30;

9.2.4.3、获取元素

元素类型 变量名 = 数组名[index];

9.2.5、数组中常见的异常

  1. NullPointerException:空指针异常(空引用异常)

操作了一个尚未初始化或者没有分配内存空间的数组

  1. ArrayIndexOutOfBoundsException:数组的索引越界异常

操作的数组的索引不在[0,数组名.length-1]范围内

9.2.6、数组遍历

9.2.6.1、for循环

  int[] nums = new int[] { 1, 3, 5, 7 };
for (int index = 0; index < nums.length; index++) {
    int ele = nums[index];//index依次是 0、1、2、3
    System.out.println(ele);
    }

9.2.6.2、for-each(增强for循环)

for(数组元素类型 变量: 数组){
//TODO
}
int[] nums = new int[] { 1, 3, 5, 7 };
for (int ele : nums) {
System.out.println(ele);
}

使用for-each操作数组更简单,因为可以不关心索引,其底层原理依然是上述的for循环操作数组

9.2.7、二维数组

在之前,数组的每一个元素就是一个个的值,这种数组我们称之为一维数组。二维数组,就是数组中的每一个元素是另一个一维数组

9.2.7.1、二维数组的定义和初始化

静态

public class ArrayInArrayDemo1 {
public static void main(String[] args) {
//定义三个一维数组
int[] arr1 = { 1, 2, 3 };
int[] arr2 = { 4, 5 };
int[] arr3 = { 6 };
//把三个一维数组存储到另一个数组中,那么该数组就是二维数组
int[][] arr = new int[][] { arr1, arr2, arr3 };
}
}

二维数组中的元素类型是一维数组,把数组元素类型[]看成一个整体,表示数据类型

动态

数组元素类型[][] 数组名 = new 数组元素类型[x][y];
x表示二维数组中有几个一维数组
y表示每一个一维数组中有几个元素。
int[][] arr = new int[3][5];

9.2.7.2、获取二维数组的元素

for循环

for (int index = 0; index < arr.length; index++) {
//取出每一个一维数组
int[] arr2= arr[index];
//迭代一维数组
for (int j = 0; j < arr2.length; j++) {
int ele = arr2[j];
System.out.println(ele);
}
System.out.println("-----");
}

for-each

for (int[] arr2 : arr) {
  //arr2为每次遍历出来的一维数组
for (int ele : arr2) {
    //ele为从arr2一维数组中遍历出来的元素
System.out.println(ele);
}
System.out.println("-----");
}

十、方法

10.1、方法的定义

方法:为了完成某一特定功能(如:求和,统计数量等)的代码块

语法格式:

修饰符] 返回值类型 方法名称(参数类型 参数名1,参数类型 参数名2,…)
{
方法体;
[return 返回值;]
}

格式分析:

  • 修饰符:public、static等,static修饰的方法直接使用类名调用即可,用static修饰的方法都属于类的
  • 返回值类型:限定返回值的类型,方法在完成一个功能后,是否需要给调用者返回一个结果?
    1. 如果需要给调用者返回结果,就写上返回数据的类型
    2. 如果不需要给调用者返回结果,就用关键字void,表示没有返回结果
  • 方法名称:用于调用方法,遵循标识符规范,首字母小写,采用驼峰命名法,见名知意
  • 形式参数:方法中圆括号中的变量,可以有多个形式参数
  • 方法体:编写如何完成该功能的代码
  • return关键字的作用
    1. 把返回值给方法的调用者
    2. 结束该方法,在return后不可以再学任何语句
    3. 当方法体中没有return时,方法的返回值类型必须为void
  • 实际参数:在调用某一个具体方法1时,实际传递的参数值
  • 如果一个需要返回值,那么一定要保证在任何条件下都必须得有返回值

注意事项

  1. 方法必须定义到类中,在java中最小的程序单元是类
  2. 一个类中可以定义多个方法
  3. 方法和方法之间是平行的,不能在一个方法中定义另一个方法(相当于不可以在一个房子里面建房子)
  4. 方法定义没有先后顺序

10.2、方法的调用

如果方法有static修饰,可以直接用方法所在的类的类名调用,如果没有static修饰,那么必须使用实例化对象来调用

10.3、方法的重载

参数列表:参数的类型+参数的个数+参数的顺序

方法签名:方法名称+方法的参数列表

在同一个类中,方法签名是唯一的,否则编译报错

方法的重载:在同一个类中,允许某方法存在一个或者多个同名的方法,但是必须参数列表不同

10.3.1、如何是否是方法重载

方法重载判断的原则:两同一不同

两同:在同一个类中方法名相同

一不同:方法的参数列表不同(参数类型、参数个数、参数顺序),只要参数类型、参数个数、参数顺序其中有一个不同,都成为参数列表不同

方法的重载和返回值类型无关,只是一般都要求返回值类型相同

10.3.2、方法重载的作用

解决了同一功能的方法由于参数不同所造成的方法名称不同而需要重复命名的问题