Java - 从零学起(一)

目录

第一节:HelloWorld

1.1 - 程序开发步骤说明

1.2 - 编写java源程序

1.3 - 编译java源文件

1.4 - 运行Java程序

1.5 - 入门程序说明

编译和运行是两回事

关于main方法

1.6 - 添加注释comment

1.7 - 关键字keywords

1.8 - 标识符

2 - 常量、变量和数据类型

2.1 - 常量

2.1.1 - 常量概述

2.1.2 - 常量分类

2.2 - 变量

2.2.1 - 变量概述

2.2.2 - 数据类型

2.2.3 - 变量的定义

3 - 数据类型转换、运算符、方法入门

3.1 - 数据类型转换

3.1.1 - 自动转换

3.1.2 - 强制转换

3.1.3 - ASCII编码表

3.2 - 运算符

3.2.1 - 算术运算符

3.2.2 - 赋值运算符

3.2.3 - 比较运算符

3.2.4 - 逻辑运算符

3.2.5 - 三元运算符

3.3 - 方法入门

3.3.1 - 概述

3.3.2 - 方法的定义

3.3.3 - 方法的调用

3.3.4 - 注意事项

4  - 流程控制语句

4.1 - 流程控制

4.1.1 - 概述

4.1.2 - 顺序结构

4.2 - 判断语句

4.2.1 - 判断语句1 - if

4.2.2 - 判断语句2 - if...else

4.2.3 - 判断语句3 - if..else if ... else

4.2.4 - if语句和三元运算符的互换

4.3 - 选择语句

4.3.1 - 选择语句 - switch

4.3.2 - case的穿透性

4.4 - 循环语句

4.4.1 - 循环概述

4.4.2 - 循环语句1 - for

4.2.3 - 循环语句2 - while

4.2.4 - 循环语句3 - do...while

4.2.5 - 循环语句区别

4.2.6 - 跳出语句

break

continue

4.5 - 扩展知识

4.5.1 - 死循环

4.5.2 - 嵌套循环

5 - 开发工具Intelij IDEA

5.1 - 开发工具概述

5.2 - IDEA软件首次驱动

5.3 - 创建包和类

5.4 - 字体设置

5.5 - IDEA的项目目录

5.6 - IDEA常用快捷键

6 - 方法

6.1 - 定义方法的格式详解

6.2 - 定义方法的两个明确

6.3 - 定义方法的注意事项

6.4 - 调用方法的三种形式

6.5 - 方法重载

7 - 数组

7.1 - 数组定义和访问

7.1.1 - 容器概述

7.1.2 - 数组的定义方式

7.1.3 - 数组的访问

7.2 - 数组原理内存

7.2.1 - 内存概述

7.2.2 - Java虚拟机的内存划分

7.2.3 - 数组在内存中的存储

7.3 - 数组的常见操作

7.3.1 - 数组越界异常

7.3.2 - 数组空指针异常

7.3.3 - 数组遍历【重点】

7.3.4 - 数组获取最大值元素

7.3.5 - 数组反转

7.4 - 数组作为方法参数和返回值

7.4.1 - 数组作为方法参数

7.4.2 - 数组作为方法返回值

7.4.3 - 方法的参数类型区别

8 - 类与对象

8.1 - 面向对象思想概述

8.1.1 - 概述

8.1.2 - 特点

8.2 - 类和对象

8.2.1 - 什么是类

8.2.2 - 什么是对象

8.2.3 - 类与对象的关系

8.3 - 类的定义

8.3.1 - 事物与类的对比

8.3.2 - 类的定义格式

8.4 - 对象的使用

8.4.1 - 对象的使用格式

8.4.2 - 成员变量的默认值

8.5 - 对象内存

8.5.1 - 一个对象,调用一个方法内存图

8.5.2 - 两个对象,调用同一个方法内存图

8.5.3 - 一个引用,作为参数传递到方法中内存图

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

9 - 封装

9.1 - 封装概述

9.1.1 - 概述

9.1.2 - 原则

9.2 - 封装的步骤

9.3 - 封装的操作 —— private关键字

9.3.1 - private的含义

9.3.2 - private的使用格式

9.4 - 封装优化1 —— this关键字

9.4.1 - this的含义

9.4.2 - this的使用格式

9.5 - 封装优化2 —— 构造方法

9.5.1 - 构造方法的定义格式

9.5.2 - 注意事项

9.6 - 标准代码 —— JavaBean

10 - Scanner类、Random类、ArrayList类

10.1 - Scanner类

10.1.1 - 什么是Scanner类

10.1.2 - 引用类型使用步骤

10.1.3 - Scanner使用步骤

10.1.4 - 练习

10.1.5 - 匿名对象

10.2 - Random类

10.2.1 - 什么是Random类

10.2.2 - Random使用步骤

10.2.3 - 练习

10.3 - ArrayList类

10.3.1 - 引入——对象数组

10.3.2 - 什么是ArrayList类

10.3.3 - ArrayList使用步骤

10.3.4 - 常用方法和遍历

10.3.5 - 如何存储基本数据类型

10.3.6 - ArrayList练习

11 - String类、static关键字、Arrays类、Math类

11.1 - String类

11.1.1 - 概述及特点

11.1.2 - 使用步骤

11.1.3 - 常用方法

11.1.4 - 练习

11.2 - static关键字

11.2.1 - 概述

11.2.2 - 定义和使用方式

11.2.3 - 静态原理图解

11.2.4 - 静态代码块

11.3 - Arrays类

11.3.1 - 概述

11.3.2 - 操作数组的方法

11.3.3 - 练习

11.4 - Math类

11.4.1 - 概述

11.4.2 - 基本运算方法

11.4.3 - 练习

12 - 继承、抽象类

12.1 - 继承

12.1.1 - 概述

12.1.2 - 继承的格式

12.1.3 - 继承后的特点 —— 成员变量

12.1.4 - 继承后的特点 —— 成员方法

12.1.5 - 继承后的特点 —— 构造方法

12.1.6 - super 和 this

12.1.7 - 继承的特点

12.2 - 抽象类

12.2.1 - 概述

12.2.2 - abstract使用格式

12.2.3 - 注意事项

12.2.4 - 综合案例

13 - 接口、多态

13.1 - 接口

13.1.1 - 概述

13.1.2 - 定义格式

13.1.3 - 基本的实现

13.1.4 - 接口的多实现 

13.1.5 - 接口的多继承

13.1.6 - 其他成员特点

13.2 - 多态

13.2.1 - 概述

13.2.2 - 多态的体现

13.2.3 - 多态的好处

13.2.4 - 引用类型转换

13.3 - 接口多态的综合案例

14 - final、权限、内部类、引用类型

14.1 - final关键字

14.1.1 - 概述

14.1.2 - 使用方式

14.2 - 权限修饰符

14.2.1 - 概述

14.2.2 - 不同权限的访问能力

14.3 - 内部类

14.3.1 - 概述

14.3.2 - 匿名内部类

14.4 - 引用类型用法总结

14.4.1 - class作为成员变量

14.4.2 - interface作为成员变量

14.4.3 - interface作为方法参数和返回值

 


第一节:HelloWorld

 

1.1 - 程序开发步骤说明

Java程序开发三步骤:编写、编译、运行。

1.2 - 编写java源程序

  1. d:\day01目录下新建文本文件,完整的文件名修改为HelloWorld.java,其中文件名称的后缀必须为.java。
  2. 使用notpad++记事本软件打开刚才新建的文本文件。
  3. 在文件中键入文本并保存,代码如下: 
public class HelloWorld{
    public static void main(String[] args){
        system.out.print("HelloWorld");
    }
}

第一个HelloWorld源程序就编写完成了,但是这个文件是程序员编写的,JVM是看不懂的,也就不能运行,因此我们必须将编写好的Java源文件编译成JVM可以看懂的字节码文件

1.3 - 编译java源文件

在DOS命令行中,进入java源文件的目录,使用javac命令进行编译。

命令:
javac java源文件名.后缀名

举例:
javac HelloWorld.java

编译成功后,命令行没有任何提示,打开d:\day01目录,发现产生了一个新文件HelloWorld.class,该文件就是编译后的文件,是Java的可运行文件,被称为字节码文件,有了字节码文件,就可以运行程序了。

java源文件的编译工具javac.exe,在JDK安装目录的bin目录下,但是由于配置了环境变量,可以在任意目录下使用。

1.4 - 运行Java程序

在DOS命令中,进入java源文件的目录,使用java命令进行运行。

命令:
java 类名字

举例:
java HelloWorld

java HelloWorld 不要写 .class!!!!

1.5 - 入门程序说明

编译和运行是两回事

编译:是指我们编写的java源文件翻译成JVM认识的class文件,在这个过程中,javac编译器会检查我们的程序是否有错误,有错误就会提示出来,如果没有错误就会编译成功。

运行:支持将class文件交给JVM去运行,此时JVM就会去执行我们编写的程序了。

关于main方法

main方法:称为主方法。写法是固定格式不可以更改。main方法是程序的入口点或者起始点,无论我们编写多少程序,JVM在运行的时候,都会从main方法这里开始执行。

1.6 - 添加注释comment

注释:就是对代码的解释和说明,其目的是让人们能够更加轻松的了解代码,为代码添加注释是十分必要的,它不影响程序的编译和运行。

Java中有单行注释和多行注释。单行注释以 // 开头,换行结束。 多行注释以 /* 开头 以 */ 结束

1.7 - 关键字keywords

关键字:是指在程序中,java已经定义好的单词,具有特殊含义。关键字比较多,不能死记硬背,学到哪里记到哪里即可。

1.8 - 标识符

标识符:是指在程序中,我们自己定义的内容,比如类的名字、方法的名字和变量的名字等等。

命名规则:硬性要求!

  • 标识符可以包含 英文字母26个(区分大小写) 、 0-9数字 、 $(美元符号) 和 _(下划线) 。
  • 标识符不能以数字开头。
  • 标识符不能是关键字。

命名规范:软性建议

  • 类名规范:首字母大写,后面每个单词首字母大写(大驼峰式)。

  • 方法名规范: 首字母小写,后面每个单词首字母大写(小驼峰式)。

  • 变量名规范:全部小写。

 

2 - 常量、变量和数据类型

2.1 - 常量

2.1.1 - 常量概述

常量:是指在java程序中固定不变的数据。

2.1.2 - 常量分类

类型含义数据举例
整数常量所有的整数0,1,2,3,4,5
小数常量所有的小数0.0,-0.1,2.55
字符常量单引号引起来,只能写一个字符,必须有内容‘a’,' ','好'
字符串常量双引号引起来,可以写多个字符,也可以不写“A”,“Hello”,“你好”
布尔常量只有两个值true,false
空常量只有一个值null

2.2 - 变量

2.2.1 - 变量概述

变量:常量是固定不变的数据,那么在程序中可以变化的量被称为变量。

java中要求,一个变量每次只能保存一个数据,必须要明确保存的数据类型。

2.2.2 - 数据类型

java的数据类型分为两大类:基本数据类型(整数、浮点数、字符、布尔)和引用数据类型(类、数组、接口)。

数据类型关键字内存占用
字节型byte1个字节
短整型short2个字节
整形int(默认)4个字节
长整型long8个字节
单精度浮点数float4个字节
双精度浮点数double(默认)8个字节
字符型char2个字节
布尔类型boolean1个字节

2.2.3 - 变量的定义

格式:

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

public class Variable{
    public static void main(String[] args){
        byte b = 100;                // 定义字节型变量
        System.out.println(b);
        short s = 1000;              // 定义短整型变量
        Ststem.out.println(s);
        int i = 123456;              // 定义整形变量
        Ststem.out.println(i);
        long l = 1233245L;           // 定义长整型变量
        Ststem.out.println(l);
        float f = 5.5f;              // 定义单精度浮点型变量
        Ststem.out.println(f);
        double d = 8.5;              // 定义双精度浮点型变量
        Ststem.out.println(d);
        boolean bool = false;        // 定义布尔型变量
        Ststem.out.println(bool);
        char c = 'A';                // 定义字符型变量
        Ststem.out.println(c);
    }
}

变量名称:在同一个大括号范围内,变量的名字不可以相同。

变量赋值:定义的变量,不赋值不能使用。

 

3 - 数据类型转换、运算符、方法入门

3.1 - 数据类型转换

java程序要求参与的计算的数据必须要保持数据的一致性,如果数据类型不一致将发生类型的转换。

3.1.1 - 自动转换

一个int类型变量和byte类型变量进行加法运算,运算结果,变量的类型将会是int类型,这就是出现了数据类型自动转换现象。

int i = 1;
byte b = 2;

自动转换:将取值范围小的类型自动提升为取值范围大的类型

public static void main(String[] args){
    int i = 1;
    byte b = 2;
    int j = b + i;            // byte x = b + i;  报错
    System.out.println(j);    // int类型和byte类型运算,结果是int类型
}

byte类型内存占有1个字节,在和int类型运算时会被提升为int类型,自动补充3个字节,因此计算结果最后还是int类型。

同样道理,一个int类型变量和一个double变量运算时,int类型会自动提升为double类型进行运算。

转换原则:范围小的类型向范围大的类型提升,byte、short、char运算时直接提升为int

3.1.2 - 强制转换

1.5赋值到int类型变量会产生编译失败,无法赋值。

int i = 1.5;    // 错误

double类型内存8个字节,int类型内存4个字节,1.5double类型,取值范围大于int。想要赋值成功,只有通过强制类型转换,将double类型强制转换成int类型才能赋值。

// double类型数据强制转成int,直接去掉小数点
int i = (int)1.5;

同样道理,一个short类型与1相加,我们知道会类型提升,但是还想给结果赋值给short类型变量,就需要强制类型转换。

public static void main(String[] args){
    // short类型变量,内存中2个字节
    short s = 1;
    // 编译失败
    s = s + 1;    
    /*
        出现编译失败
        s 和 1 做运算的时候,1是int类型,s 会被提升为int类型    
        s + 1 后的结果是int类型,将结果再赋值给short类型时发生错误
        short内存中2个字节,int类型4个字节
        必须将int强制转成short才能完成赋值
    */
    s = (short)(s + 1);    // 编译成功
}

需要注意!

浮点转换成整数,直接取消小数点,可能造成数据损失精度。

int 强制转成 short 砍掉两个字节,可能造成数据丢失。

3.1.3 - ASCII编码表

public static void main(String[] args){
    char c = 'a';                 // 字符类型变量
    int i = 1;                    // 字符类型和int类型计算
    System.out.println(c + i);    // 输出结果是98
}

在计算机的内部都是二进制的0、1数据,如何让计算机可以直接识别人类的文字呢?就产生了编码表的概念。

编码表:就是将人类的文字和一个十进制数字进行对应起来组成一张表格。

字符数值
048
957
A65
Z90
a97
z122

将所有的英文字母,数字,符号和十进制进行了对应,因此产生了世界上第一张编码表。

3.2 - 运算符

3.2.1 - 算术运算符

算数运算符包括: 
+加法运算,字符串连接运算
-减法运算
*乘法运算
/除法运算
%取模运算,两个数字相除取余数
++、--自增自减运算

java中,整数使用以上运算符,无论怎么激素按,也不会得到小数。

public static void main(String[] args){
    int i = 1234;
    System.out.println(i / 1000 * 1000);    // 计算结果是1000
}

++运算,变量自己增长1。反之,--运算,变量自己减少1,用法与++一致。

独立运算

变量在独立运算时,前++后++没有区别。

变量前++:例如++i

变量后++:例如i++

混合运算

和其他变量放在一起,前++和后++就产生了不同。

变量前++:变量a自己加1,将加1后的结果赋值给变量b,也就是说a先计算,a和b的结果都是2。

public static void main(String[] args){
    int a = 1;
    int b = ++a;
    System.out.println(a);    //计算结果是2
    System.out.println(b);    //计算结果是2
}

变量后++:变量a先把自己的值1,赋值给变量b,此时变量b的值就是1,变量a自己再加1,a的结果是2,b的结果是1。

public static void main(String[] args){
    int a = 1;
    int b = a++;
    System.out.println(a);    //计算结果是2
    System.out.println(b);    //计算结果是1
}

+ 符号在字符串中的操作

+ 符号在遇到字符串时,表示连接、拼接的含义。

"a" + "b" 的结果是"ab",连接含义。

public static void main(String[] args){
    System.out.println("5+5="+5+5);    // 输出5+5=55
}

3.2.2 - 赋值运算符

赋值运算符包括: 
=等于号
+=加等于
-=减等于
*=乘等于
/=除等于
%=取模等

赋值运算符,就是将符号右边的值,付给左边的变量。

public static void main(String[] args){
    int i = 5;
    i += 5;                   // 计算方式 i = i + 5 变量i先加5,再赋值变量1
    System.out.println(i);    // 输出结果是10
}

3.2.3 - 比较运算符

比较运算符包括: 
==比较符号两边数据是否相等,想等结果是true。
<比较符号左边的数据是否小于右边的数据,如果小于结果是true。
>比较符号左边的数据是否大于右边的数据,如果大于结果是true。
<=比较符号左边的数据是否小于或者等于右边的数据,如果小于结果是true。
>=比较符号左边的数据是否大于或者等于右边的数据,如果大于结果是true。
!=不等于符号,如果符号两边的数据不相等,结果是true。

比较运算符,是两个数据之间进行比较,运算结果都是布尔值true或者false

public static void main(String[] args){
    System.out.println(1 == 1);    // true
    System.out.println(1 < 2);     // true
    System.out.println(3 > 4);     // false
    System.out.println(3 <= 4);    // true
    System.out.println(3 >= 4);    // false
    System.out.println(3 != 4);    // true
}

3.2.4 - 逻辑运算符

逻辑运算符包括: 
&& 短路与

1.两边都是true,结果是true

2.一边是false,结果是false

短路特点:符号左边是false,右边不再运算

||    短路或

1.两边都是false,结果是false

2.一边是true,结果是true

短路特点:符号左边是true,右边不再运算

!  取反

1. !true 结果是 false

2. !false 结果是 true

逻辑运算符,是用来连接两个布尔类型结果的运算符,运算结果都是布尔值true或者false。

public static void main(String[] args){
    System.out.println(true && true);         // true
    System.out.println(true && false);        // false
    System.out.println(false && true);        // false, 右边不计算

    System.out.println(false || false);       // false
    System.out.println(false || true);        // true
    System.out.println(true || false);        // true

    System.out.println(!false);               // true
}

3.2.5 - 三元运算符

三元运算符格式:
数据类型 变量名 = 布尔类型表达式 ? 结果1 : 结果2

三元运算符计算方式:

布尔类型表达式结果是true,三元运算符整体结果为结果1,赋值给变量。

布尔类型表达式结果是false,三元运算符整体结果为结果2,赋值给变量。

public static void main(String[] args){
    int i = (1 == 2 ? 100 : 200);
    System.out.println(i);            // 200
    int j = (3 <= 4 ? 500 : 600);        
    System.out.println(j);            // 500
}

3.3 - 方法入门

3.3.1 - 概述

我们在学习运算符的时候,都为每个运算符单独创建一个新的类和main方法,我们会发现这样编写代码非常的繁琐,重复的代码过多,所以就需要使用方法来实现。

方法:就是将一个功能抽取出来,把代码单独定义在一个大括号内,形成一个单独的功能。

当我们需要这个功能的时候,就可以去调用。这样即实现了代码的复用性,也解决了代码冗余的现象。

3.3.2 - 方法的定义

定义格式:
修饰符 返回值类型 方法名 ( 参数列表 ) {
    代码 ...
    return;
}

定义格式解释:

修饰符:目前固定写法 public static。

返回类型:目前固定写法 void ,其他返回值类型会在后面讲解。

方法名:为我们定义的方法起名,满足标识符的规范,用来调用方法。

参数列表:目前无参数,带有参数的方法在后面讲解。

return:方法结束,因为返回类型是void,方法大括号内的return可以不写。

public static void methodName(){
    System.out.println("这是一个方法");
}

3.3.3 - 方法的调用

方法在定义完毕后,方法不会自己运行,必须被调用才能执行,我们可以在主方法main中来调用我们自己定义好的方法。在主方法中,直接写要调用的方法名字就可以调用了。

public static void main(String[] args){
    method();    // 调用定义的方法method
}

// 定义方法,被main方法调用
public static void method(){
    System.out.println("自己定义的方法,需要被main调用运行");
}

3.3.4 - 注意事项

方法必须定义在一类中方法外。

方法不能定义在另一个方法的里面。

public class Demo(){
    public static void main(String[] args){
        
    }

    // 正确写法,类中,main方法外面可以定义方法
    public static void method(){}
}
public class Demo(){
    public static void main(String[] args){
        // 错误写法:一个方法不能定义在另一个方法内部
        public static void method(){}
    }
}

 

4  - 流程控制语句

4.1 - 流程控制

4.1.1 - 概述

在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。也就是说,程序的流程对运行结果
有直接的影响。所以,我们必须清楚每条语句的执行流程。而且,很多时候我们要通过控制语句的执行顺序来实现
我们要完成的功能。

4.1.2 - 顺序结构

public static void main(String[] args){
    //顺序执行,根据编写的顺序,从上到下运行
    System.out.println(1);
    System.out.println(2);
    System.out.println(3);
}

4.2 - 判断语句

4.2.1 - 判断语句1 - if

if(关系表达式){
    语句体;
}

执行流程

首先判断关系表达式看其结果是true还是false。

如果是true就执行语句体。

如果是false就不执行语句体。

public static void main(String[] args){
    System.out.println("开始");
    
    // 定义两个变量
    int a = 10;
    int b = 20;

    //变量使用if判断
    if (a == b){
        System.out.println("a等于b");
    } 
    
    int c = 10;
    
    if(a == c){
        System.out.println("a等于c");
    } 
    
    System.out.println("结束");
}

4.2.2 - 判断语句2 - if...else

if(关系表达式) {
    语句体1;
}else {
    语句体2;
}

执行流程

首先判断关系表达式看其结果是true还是false

如果是true就执行语句体1

如果是false就执行语句体2

public static void main(String[] args){
    // 判断给定的数据是奇数还是偶数
    // 定义变量
    int a = 1;
    if(a % 2 == 0) {
        System.out.println("a是偶数");
    } else{
        System.out.println("a是奇数");
    } 
    System.out.println("结束");
}

4.2.3 - 判断语句3 - if..else if ... else

if (判断条件1) {
    执行语句1;
} else if (判断条件2) {
    执行语句2;
} 
...
}else if (判断条件n) {
    执行语句n;
} else {
    执行语句n+1;
}

执行流程

首先判断关系表达式1看其结果是true还是false

如果是true就执行语句体1

如果是false就继续判断关系表达式2看其结果是true还是false

如果是true就执行语句体2

如果是false就继续判断关系表达式…看其结果是true还是false

如果没有任何关系表达式为true,就执行语句体n+1。

public static void main(String[] args) {
    // x和y的关系满足如下:
    // x>=3 y = 2x + 1;
    //‐1<=x<3 y = 2x;
    // x<=‐1 y = 2x – 1;
    // 根据给定的x的值,计算出y的值并输出。
    // 定义变量
    int x = 5;
    int y;
    if (x>= 3) {
        y = 2 * x + 1;
    } else if (x >= ‐1 && x < 3) {
        y = 2 * x;
    } else {
        y = 2 * x ‐ 1;
    } 
    System.out.println("y的值是:"+y);
}

4.2.4 - if语句和三元运算符的互换

在某些简单的应用中,if语句是可以和三元运算符互换使用的。

public static void main(String[] args) {
    int a = 10;
    int b = 20;
    //定义变量,保存a和b的较大值
    int c;
    if(a > b) {
        c = a;
    } else {
        c = b;
    } 
    //可以上述功能改写为三元运算符形式
    c = a > b ? a:b;
}

4.3 - 选择语句

4.3.1 - 选择语句 - switch

switch语句格式:
switch(表达式) {
    case 常量值1:
        语句体1;
        break;
    case 常量值2:
        语句体2;
        break;
    ...
    default:
        语句体n+1;
        break;
}

执行流程

首先计算出表达式的值

其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结
束。

最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉
 

public static void main(String[] args) {
    //定义变量,判断是星期几
    int weekday = 6;
    //switch语句实现选择
    switch(weekday) {
        case 1:
            System.out.println("星期一");
            break;
        case 2:
            System.out.println("星期二");
            break;
        case 3:
            System.out.println("星期三");
            break;
        case 4:
            System.out.println("星期四");
            break;
        case 5:
            System.out.println("星期五");
            break;
        case 6:
            System.out.println("星期六");
            break;
        case 7:
            System.out.println("星期日");
            break;
        default:
            System.out.println("你输入的数字有误");
            break;
    }
}

switch语句中,表达式的数据类型,可以是byte,short,int,char,enum(枚举),JDK7后可以接收字符串

4.3.2 - case的穿透性

在switch语句中,如果case的后面不写break,将出现穿透现象,也就是不会在判断下一个case的值,直接向后运
行,直到遇到break,或者整体switch结束。

public static void main(String[] args) {
    int i = 5;
    switch (i){
        case 0:
            System.out.println("执行case0");
            break;
        case 5:
            System.out.println("执行case5");
        case 10:
            System.out.println("执行case10");
        default:
            System.out.println("执行default");
    }
}

上述程序中,执行case5后,由于没有break语句,程序会一直向后走,不会在判断case,也不会理会break,直接
运行完整体switch。

由于case存在穿透性,因此初学者在编写switch语句时,必须要写上break

4.4 - 循环语句

4.4.1 - 循环概述

循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复
执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形
成死循环。

4.4.2 - 循环语句1 - for

for(初始化表达式①; 布尔表达式②; 步进表达式④){
    循环体③
}

执行流程

执行顺序:①②③④>②③④>②③④…②不满足为止。

①负责完成循环变量初始化

②负责判断是否满足循环条件,不满足则跳出循环

③具体执行的语句

④循环后,循环条件所涉及变量的变化情况

public static void main(String[] args) {
    //控制台输出10次HelloWorld,不使用循环
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("HelloWorld");
    System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐");
    //用循环改进,循环10次
    //定义变量从0开始,循环条件为<10
    for(int x = 0; x < 10; x++) {
        System.out.println("HelloWorld"+x);
    }
}

4.2.3 - 循环语句2 - while

初始化表达式①
    while(布尔表达式②){
        循环体③
        步进表达式④
    }

执行流程

执行顺序:①②③④>②③④>②③④…②不满足为止。

①负责完成循环变量初始化。

②负责判断是否满足循环条件,不满足则跳出循环。

③具体执行的语句。

④循环后,循环变量的变化情况。

public static void main(String[] args) {
    //while循环实现打印10次HelloWorld
    //定义初始化变量
    int i = 1;
    //循环条件<=10
    while(i<=10){
        System.out.println("HelloWorld");
        //步进
        i++;
    }
}

4.2.4 - 循环语句3 - do...while

初始化表达式①
    do{
        循环体③
        步进表达式④
    }while(布尔表达式②);

执行流程

执行顺序:①③④>②③④>②③④…②不满足为止。

①负责完成循环变量初始化。

②负责判断是否满足循环条件,不满足则跳出循环。

③具体执行的语句

④循环后,循环变量的变化情况

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

do...while循环的特点:无条件执行一次循环体,即使我们将循环条件直接写成false,也依然会循环一次。这样的
循环具有一定的风险性,因此初学者不建议使用do...while循环。

4.2.5 - 循环语句区别

for 和 while 的小区别:

控制条件语句所控制的那个变量,在for循环结束后,就不能再被访问到了,而while循环结束还可以继
续使用,如果你想继续使用,就用while,否则推荐使用for。原因是for循环结束,该变量就从内存中消
失,能够提高内存的使用效率。

在已知循环次数的时候使用推荐使用for,循环次数未知的时推荐使用while

4.2.6 - 跳出语句

break

使用场景:终止switch或者循环

public static void main(String[] args) {
    for (int i = 1; i<=10; i++) {
        //需求:打印完两次HelloWorld之后结束循环
        if(i == 3){
            break;
        } 
        System.out.println("HelloWorld"+i);
    }
}

continue

使用场景:结束本次循环,继续下一次循环

public static void main(String[] args) {
    for (int i = 1; i <= 10; i++) {
        //需求:不打印第三次HelloWorld
        if(i == 3){
            continue;
        } 
        System.out.println("HelloWorld"+i);
    }
}

4.5 - 扩展知识

4.5.1 - 死循环

死循环:也就是循环中的条件永远为true,死循环的是永不结束的循环。例如:while(true){}。

4.5.2 - 嵌套循环

所谓嵌套循环,是指一个循环的循环体是另一个循环。比如for循环里面还有一个for循环,就是嵌套循环。总
共的循环次数=外循环次数*内循环次数

for(初始化表达式①; 循环条件②; 步进表达式⑦) {
    for(初始化表达式③; 循环条件④; 步进表达式⑥) {
        执行语句⑤;
    }
}

嵌套循环执行流程

执行顺序:①②③④⑤⑥>④⑤⑥>⑦②③④⑤⑥>④⑤⑥

外循环一次,内循环多次。

比如跳绳:一共跳5组,每组跳10个。5组就是外循环,10个就是内循环。

public static void main(String[] args) {
    //5*8的矩形,打印5行*号,每行8个
    //外循环5次,内循环8次
    for(int i = 0; i < 5; i++){
        for(int j = 0; j < 8; j++){
            //不换行打印星号
            System.out.print("*");
        } 
        //内循环打印8个星号后,需要一次换行
        System.out.println();
    }
}


5 - 开发工具Intelij IDEA

5.1 - 开发工具概述

IDEA是一个专门针对Java的集成开发工具(IDE),由Java语言编写。所以,需要有JRE运行环境并配置好环境变量。
它可以极大地提升我们的开发效率。可以自动编译,检查错误。在公司中,使用的就是IDEA进行开发。

5.2 - IDEA软件首次驱动

5.3 - 创建包和类

5.4 - 字体设置

5.5 - IDEA的项目目录

我们创建的项目,在d:\ideawork目录的demo下

.idea 目录和 demo.iml 和我们开发无关,是IDEA工具自己使用的

out 目录是存储编译后的.class文件

src 目录是存储我们编写的.java源文件

5.6 - IDEA常用快捷键

快捷键功能
Alt+Enter导入包,自动修正代码
Ctrl+Y删除光标所在行
Ctrl+D复制光标所在行的内容,插入光标位置下面
Ctrl+Alt+L格式化代码
Ctrl+/单行注释
Ctrl+Shift+/选中代码注释,多行注释,再按取消注释
Alt+Ins自动生成代码,toString,get,set等方法
Alt+Shift+上下箭头移动当前代码行

 

6 - 方法

6.1 - 定义方法的格式详解

修饰符 返回值类型 方法名(参数列表){
    //代码省略...
    return 结果;
}

修饰符: public static 固定写法

返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者

参数列表:方法在运算过程中的未知数据,调用者调用方法时传递

return:将方法执行后的结果带给调用者,方法执行到 return ,整体方法运行结束

6.2 - 定义方法的两个明确

需求:定义方法实现两个整数的求和计算。

明确返回值类型:方法计算的是整数的求和,结果也必然是个整数,返回值类型定义为int类型。

明确参数列表:计算哪两个整数的和,并不清楚,但可以确定是整数,参数列表可以定义两个int类型的
变量,由调用者调用方法时传递

public class Method_Demo2 {
    public static void main(String[] args) {
        // 调用方法getSum,传递两个整数,这里传递的实际数据又称为实际参数
        // 并接收方法计算后的结果,返回值
        int sum = getSum(5, 6);
        System.out.println(sum);
    } 

    /* 定义
    计算两个整数和的方法
    返回值类型,计算结果是int
    参数:不确定数据求和,定义int参数.参数又称为形式参数
    */
    public static int getSum(int a, int b) {
        return a + b;
    }
}

程序执行,主方法 main 调用 getSum 方法,传递了实际数据 5和6 ,两个变量 a和b 接收到的就是实际参数,并
将计算后的结果返回,主方法 main 中的变量 sum 接收的就是方法的返回值。

6.3 - 定义方法的注意事项

定义位置:类中方法外面。

返回值类型:必须要和return语句返回的类型相同,否则编译失败。

// 返回值类型要求是int
public static int getSum() {
    return 5;// 正确,int类型
    return 1.2;// 错误,类型不匹配
    return true;// 错误,类型不匹配
}

不能在 return 后面写代码, return 意味着方法结束,所有后面的代码永远不会执行,属于无效代码。

public static int getSum(int a,int b) {
    return a + b;
    System.out.println("Hello");// 错误,return已经结束,这里不会执行,无效代码
}

6.4 - 调用方法的三种形式

直接调用:直接写方法名调用

public static void main(String[] args) {
    print();
} 
public static void print() {
    System.out.println("方法被调用");
}

赋值调用:调用方法,在方法前面定义变量,接受方法返回值。

public static void main(String[] args) {
    int sum = getSum(5,6);
    System.out.println(sum);
} 
public static int getSum(int a,int b) {
    return a + b;
}

输出语句调用:在输出语句中调用方法,System.out,println(方法名())。

public static void main(String[] args) {
    System.out.println(getSum(5,6));
} 
public static int getSum(int a,int b) {
    return a + b;
}

不能用输出语句调用void类型方法,因为方法执行后没有结果,也就打印不出任何内容。

6.5 - 方法重载

指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返
回值类型无关。

参数列表:个数不同,数据类型不同,顺序不同。

重载方法调用:JVM通过方法的参数列表,调用不同的方法

public class Method_Demo6 {
public static void main(String[] args) {
    //定义不同数据类型的变量
    byte a = 10;
    byte b = 20;
    short c = 10;
    short d = 20;
    int e = 10;
    int f = 10;
    long g = 10;
    long h = 20;

    // 调用
    System.out.println(compare(a, b));
    System.out.println(compare(c, d));
    System.out.println(compare(e, f));
    System.out.println(compare(g, h));
    } 
    
    // 两个byte类型的
    public static boolean compare(byte a, byte b) {
        System.out.println("byte");
        return a == b;
    } 

    // 两个short类型的
    public static boolean compare(short a, short b) {
        System.out.println("short");
        return a == b;
    } 

    // 两个int类型的
    public static boolean compare(int a, int b) {
        System.out.println("int");
        return a == b;
    } 

    // 两个long类型的
    public static boolean compare(long a, long b) {
        System.out.println("long");
        return a == b;
    }
}

 

7 - 数组

7.1 - 数组定义和访问

7.1.1 - 容器概述

容器概念:是将多个数据存储到一起,每个数据称为该容器的元素。

数组概念:数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致。

7.1.2 - 数组的定义方式

方式一

数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];

数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
[] : 表示数组。
数组名字:为定义的数组起个变量名,满足标识符规范,可以使用名字操作数组。
new:关键字,创建数组使用的关键字。
数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
[长度]:数组的长度,表示数组容器中可以存储多少个元素。

// 定义可以存储三个整数的数组容器,代码如下:
int[] arr = new int[3];

注意!数组有定长特性,长度一旦指定,不可更改。

方式二

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

// 定义存储1,2,3,4,5整数的数组容器
int[] arr = new int[]{1,2,3,4,5};

方式三

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

//定义存储1,2,3,4,5整数的数组容器
int[] arr = {1,2,3,4,5};

7.1.3 - 数组的访问

索引: 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引
(index),可以通过数组的索引访问到数组中的元素。

数组名[索引]

数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的
长度,语句为: 数组名.length ,属性length的执行结果是数组的长度,int类型结果。由次可以推断出,数
组的最大索引值为 数组名.length-1 。

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

索引访问数组中的元素

数组名[索引]=数值,为数组中的元素赋值

变量=数组名[索引],获取出数组中的元素

public static void main(String[] args){
    int[] arr = {1,2,3,4,5};       // 定义存储int类型数组,赋值元素1,2,3,4,5
    arr[0] = 6;                    // 为0索引元素赋值为6
    int i = arr[0];                // 获取数组0索引上的元素
    System.out.println(i);         
    System.out.println(arr[0]);    // 直接输出数组0索引元素
}

7.2 - 数组原理内存

7.2.1 - 内存概述

内存是计算机中的重要原件,临时存储区域,作用是运行程序。我们编写的程序是存放在硬盘中的,在硬盘中的程
序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。

Java虚拟机要运行程序,必须要对内存进行空间的分配和管理

7.2.2 - Java虚拟机的内存划分

为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

区域名称作用
寄存器给CPU使用,和我们开发无关。
本地方法栈JVM在使用操作系统功能的时候使用,和我们开发无关。
方法区存储可以运行的class文件。
堆内存存储对象或者数组,new来创建的,都存储在堆内存。
方法栈方法运行时使用的内存,比如main方法运行,进入方法栈中执行。

7.2.3 - 数组在内存中的存储

public static void main(String[] args) {
    int[] arr = new int[3];
    System.out.println(arr);//[I@5f150435
}

以上方法执行,输出的结果是[I@5f150435,这个是什么呢?是数组在内存中的地址。new出来的内容,都是在堆
内存中存储的,而方法中的变量arr保存的是数组的地址。

输出arr[0],就会输出arr保存的内存地址中数组中0索引上的元素。

public static void main(String[] args) {
    int[] arr = new int[3];
    int[] arr2 = new int[2];
    System.out.println(arr);
    System.out.println(arr2);
}

public static void main(String[] args) {
    // 定义数组,存储3个元素
    int[] arr = new int[3];
    //数组索引进行赋值
    arr[0] = 5;
    arr[1] = 6;
    arr[2] = 7;
    //输出3个索引上的元素值
    System.out.println(arr[0]);
    System.out.println(arr[1]);
    System.out.println(arr[2]);
    //定义数组变量arr2,将arr的地址赋值给arr2
    int[] arr2 = arr;
    arr2[1] = 9;
    System.out.println(arr[1]);
}

7.3 - 数组的常见操作

7.3.1 - 数组越界异常

public static void main(String[] args) {
    int[] arr = {1,2,3};
    System.out.println(arr[3]);
}

创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运
行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一
旦出现了,就必须要修改我们编写的代码。

7.3.2 - 数组空指针异常

public static void main(String[] args) {
    int[] arr = {1,2,3};
    arr = null;
    System.out.println(arr[0]);
}

arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候
会抛出 NullPointerException 空指针异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修
改我们编写的代码

7.3.3 - 数组遍历【重点】

数组遍历: 就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。

public static void main(String[] args) {
    int[] arr = { 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]);
}

以上代码是可以将数组中每个元素全部遍历出来,但是如果数组元素非常多,这种写法肯定不行,因此我们需要改
造成循环的写法。数组的索引是 0 到 lenght-1 ,可以作为循环的条件出现。

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

7.3.4 - 数组获取最大值元素

最大值获取:从数组的所有元素中找出最大值。

实现思路

1 - 定义变量,保存数组0索引上的元素

2 - 遍历数组,获取出数组中的每个元素

3 - 将遍历到的元素和保存数组0索引上值的变量进行比较

4 - 如果数组元素的值大于了变量的值,变量记录住新的值

5 - 数组循环遍历结束,变量保存的就是数组中的最大值

public static void main(String[] args) {
    int[] arr = { 5, 15, 2000, 10000, 100, 4000 };
    //定义变量,保存数组中0索引的元素
    int max = arr[0];
    //遍历数组,取出每个元素
    for (int i = 0; i < arr.length; i++) {
        //遍历到的元素和变量max比较
        //如果数组元素大于max
        if (arr[i] > max) {
            //max记录住大值
            max = arr[i];
        }
    } 
    System.out.println("数组最大值是: " + max);
}

7.3.5 - 数组反转

数组的反转: 数组中的元素颠倒顺序,例如原始数组为1,2,3,4,5,反转后的数组为5,4,3,2,1

实现思想

数组最远端的元素互换位置。

实现反转,就需要将数组最远端元素位置交换

定义两个变量,保存数组的最小索引和最大索引

两个索引上的元素交换位置

最小索引++,最大索引--,再次交换位置

最小索引超过了最大索引,数组反转操作结束

public static void main(String[] args) {
    int[] arr = { 1, 2, 3, 4, 5 };
    /*
        循环中定义变量min=0最小索引
        max=arr.length‐1最大索引
        min++,max‐‐
    */
    for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) {
        //利用第三方变量完成数组中的元素交换
        int temp = arr[min];
        arr[min] = arr[max];
        arr[max] = temp;
    } 
    
    // 反转后,遍历数组
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
}

7.4 - 数组作为方法参数和返回值

7.4.1 - 数组作为方法参数

数组作为方法参数传递,传递的参数是数组内存的地址。

public static void main(String[] args) {
    int[] arr = { 1, 3, 5, 7, 9 };
        //调用方法,传递数组
        printArray(arr);
    } 

    /*
        创建方法,方法接收数组类型的参数
        进行数组的遍历
    */
public static void printArray(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
}

7.4.2 - 数组作为方法返回值

数组作为方法的返回值,返回的是数组的内存地址。

public static void main(String[] args) {
    //调用方法,接收数组的返回值
    //接收到的是数组的内存地址
    int[] arr = getArray();
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
} 

/*
    创建方法,返回值是数组类型
    return返回数组的地址
*/
public static int[] getArray() {
    int[] arr = { 1, 3, 5, 7, 9 };
    //返回数组的地址,返回到调用者
    return arr;
}

7.4.3 - 方法的参数类型区别

方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值。

 

8 - 类与对象

8.1 - 面向对象思想概述

8.1.1 - 概述

Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,
使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面
向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算
机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去
操作实现。

8.1.2 - 特点

面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。
面向对象的语言中,包含了三大基本特征,即封装、继承和多态。

8.2 - 类和对象

8.2.1 - 什么是类

:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该
类事物。

8.2.2 - 什么是对象

对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性
和行为

8.2.3 - 类与对象的关系

类是对一类事物的描述,是抽象的

对象是一类事物的实例,是具体的

类是对象的模板,对象是类的实体

8.3 - 类的定义

8.3.1 - 事物与类的对比

属性:事物的状态信息。 行为:事物能够做什么

成员变量:对应事物的属性 成员方法:对应事物的行为

8.3.2 - 类的定义格式

public class ClassName{
    // 成员变量
    // 成员方法
}

定义类:就是定义类的成员,包括成员变量和成员方法。

成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。

成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细
讲解。

public class Student{
    // 成员变量
    String name;        // 姓名
    int age;            // 年龄

    // 成员方法
    public void study(){    
        System.out.println("好好学习,天天向上.");    
    }

    public void eat(){
        System.out.println("吃饭");
    }
}

8.4 - 对象的使用

8.4.1 - 对象的使用格式

// 创建对象
类名 对象名 = new 类名();

// 使用对象访问类中成员
对象名.成员变量;
对象名.成员方法();

// 对象的使用格式举例
public void Test01_Student(){
    public static void main(String[] args){
        // 创建对象格式: 类名 对象名 = new 类名();
        Student s = new Student();
        System.out.println("s:" + s);

        // 直接输出成员变量值
        System.out.println("姓名:" + s.name);    // null
        System.out.println("年龄:" + s.age);     // 0
        System.out.println("---------------");

        // 给成员变量赋值
        s.name = "赵丽颖";
        s.age = 18;

        // 再次输出成员变量
        System.out.println("姓名:" + s.name);    // 赵丽颖
        System.out.println("年龄:" + s.age);     // 18
        System.out.println("---------------");

        // 调用成员方法
        s.study();
        s.eat();        
    }
}

8.4.2 - 成员变量的默认值

 数据类型默认值
基本类型整数(byte,short,int,long)0
 浮点数(float,double)0.0
 字符(char)’\u0000‘
 布尔(boolean)false
引用类型数组、类、接口null

8.5 - 对象内存

8.5.1 - 一个对象,调用一个方法内存图

通过上图,我们可以理解,在栈内存中运行的方法,遵循"先进后出,后进先出"的原则。变量p指向堆内存中
的空间,寻找方法信息,去执行该方法。

8.5.2 - 两个对象,调用同一个方法内存图

对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息
只保存一份,节约内存空间。

8.5.3 - 一个引用,作为参数传递到方法中内存图

引用类型作为参数,传递的是地址值。

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

在类中的位置不同 

成员变量:类中,方法外

局部变量:方法中或者方法声明上(形式参数)

作用范围不一样 

成员变量:类中

局部变量:方法中

初始化值的不同 

成员变量:有默认值

局部变量:没有默认值。必须先定义,赋值,最后使用

在内存中的位置不同 

成员变量:堆内存

局部变量:栈内存

生命周期不同 

成员变量:随着对象的创建而存在,随着对象的消失而消失

局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
 

9 - 封装

9.1 - 封装概述

9.1.1 - 概述

面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。
封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的
方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

9.1.2 - 原则

将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。

9.2 - 封装的步骤

  1. 使用private关键字来修饰成员变量
  2. 对需要访问的成员变量,提供一对对应的getXxx方法、setXxx方法。

9.3 - 封装的操作 —— private关键字

9.3.1 - private的含义

  1. private是一个权限修饰符,代表最小权限
  2. 可以修饰成员变量和成员方法
  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问

9.3.2 - private的使用格式

pirvate 数据类型 变量名;

// 使用private修饰成员变量
public class Student{
    private String name;
    private int age;
}

// 提供getXxx方法和setXxx方法,可以访问成员变量
public class Student{
    private String name;
    private int age;

    public void setName(String n){
        name = n;
    }

    public String getName(){
        return name;
    }

    public void setAge(int a){
        age = a;
    }

    public int getAge(){
        return age;
    }
}

9.4 - 封装优化1 —— this关键字

9.4.1 - this的含义

this代表所在类的当前对象的引用(地址值),即对象自己的引用。

方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。

9.4.2 - this的使用格式

// this的使用格式
this.成员变量名;

// 使用this修饰方法中的变量,解决成员变量被隐藏的问题
public class Student{
    private String name;
    private int age;

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age
    }
}

//方法中只有一个变量名时,默认也是使用this修饰,可以省略不写

9.5 - 封装优化2 —— 构造方法

当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。

无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。

9.5.1 - 构造方法的定义格式

// 构造方法的定义格式
修饰符 构造方法名(参数列表){
    方法体
}

构造方法的写法上,方法名与它所在的类名相同。它没有返回值,所以不需要返回值类型,甚至不需要void。

public class Student {
    private String name;
    private int age;

    // 无参数构造方法
    public Student() {}

    // 有参数构造方法
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }
}

9.5.2 - 注意事项

  • 如果你不提供构造方法,系统会给出无参数构造方法。
  • 如果你提供了构造方法,系统将不再提供无参数构造方法。
  • 构造方法是可以重载的,既可以定义参数,也可以不定义参数。

9.6 - 标准代码 —— JavaBean

JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无
参数的构造方法,提供用来操作成员变量的 set 和 get 方法。

public class ClassName{
    //成员变量
    //构造方法
    //无参构造方法【必须】
    //有参构造方法【建议】
    //成员方法
    //getXxx()
    //setXxx()
}

编写符合 JavaBean 规范的类,以学生类为例,标准代码如下:

public class Student {
    //成员变量
    private String name;
    private int age;

    //构造方法
    public Student() {}
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    } 

    //成员方法
    public void setName(String name) {
        this.name = name;
    } 

    public String getName() {
        return name;
    } 

    public void setAge(int age) {
        this.age = age;
    } 

    public int getAge() {
        return age;
    }
}

 

10 - Scanner类、Random类、ArrayList类

10.1 - Scanner类

10.1.1 - 什么是Scanner类

一个可以解析基本类型和字符串的简单文本扫描器。

Scanner sc = new Scanner(System.in);    // System.in 系统输入指的是通过键盘录入数据
int i = sc,nextInt();

10.1.2 - 引用类型使用步骤

导包

使用import关键字导包,在类的所有代码之前导包,引入要使用的类型,java.lang包下的所有类无需导入。

import 包名.类名;

创建对象

使用该类的构造方法,创建一个该类的对象。

数据类型 变量名 = new 数据类型(参数列表);

Scanner sc = new Scanner(System.in);

调用方法

调用该类的成员方法,完成指定的功能。

变量名.方法名();

int i = sc.nextInt();    // 接受一个键盘录入的整数

10.1.3 - Scanner使用步骤

查看类

java.util.Scanner 该类需要import导入后使用

查看构造方法

public Scanner(InputStream source) 构造一个新的Scanner,它生成的值时从指定的输入流扫描的。

查看成员方法

public int nextInt(); 将输入信息的下一个标记扫描为一个int值。

使用Scanner类,完成接受键盘录入数据的操作。

import java.util.Scanner;

public class Demo01_Scanner{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        System.out.println("请录入一个整数:");
        int i = sc.nextInt();

        System.out.println("i:" + i);    
    }
}

10.1.4 - 练习

// 键盘录入两个数据并求和
import java.util.Scanner;

public class Test01_Scanner{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入第一个数据:");
        int a = sc.nextInt();
        System.out.println("请输入第二个数据:");
        int b = sc.nextInt();
            
        int sum = a + b;
        System.out.println("sum: " + sum);
    }
}

// 键盘录入三个数据并获取最大值
import java.util.Scanner;

public class Test02_Scanner{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入第一个数据:");
        int a = sc.nextInt();
        System.out.println("请输入第二个数据:");
        int b = sc.nextInt();
        System.out.println("请输入第三个数据:");
        int c = sc.nextInt();

        int temp = (a > b ? a : b);
        int max = (temp > c ? temp : c);

        System.out.println("max: " + max);
    }
}

10.1.5 - 匿名对象

创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。虽然是创建对象的简化写法,但是应用
场景非常有限。

匿名对象:没有变量名的对象。

new 类名(参数列表);

new Scanner(System.in);
// 1.创建匿名对象直接调用方法,没有变量名
new Scanner(System.in).nextInt();

// 2.一旦调用两次方法,就是创建了两个对象,造成了浪费
new Scanner(System.in).nextInt();
new Scanner(System.in).nextInt();
// 注意! 一个匿名对象,只能使用一次。

// 3.匿名对象可以作为方法的参数和返回值
// 作为参数
class Test {
    public static void main(String[] args) {
        // 普通方式
        Scanner sc = new Scanner(System.in);
        input(sc);
        
        //匿名对象作为方法接收的参数
        input(new Scanner(System.in));
    } 
    
    public static void input(Scanner sc){
        System.out.println(sc);
    }
}

// 作为返回值
class Test2 {
    public static void main(String[] args) {
        // 普通方式
        Scanner sc = getScanner();
    } 

    public static Scanner getScanner(){
        //普通方式
        //Scanner sc = new Scanner(System.in);
        //return sc;
        //匿名对象作为方法返回值
        return new Scanner(System.in);
    }
}

10.2 - Random类

10.2.1 - 什么是Random类

此类的实例用于生成伪随机数。

Random r = new Random();
int i = r.nextInt();

10.2.2 - Random使用步骤

查看类

java.util.Random;    // 该类需要import导入后使用

查看构造方法

public Random();    // 创建一个新的随机数生成器

查看成员方法

public int nextInt(int n);    

// 返回一个伪随机数,范围在0(包括)和指定值n(不包括)之间的int值。
// 使用Random类,完成生成3个10以内的随机整数的操作

import java.util.Random;

public class Demo01_Random{
    public static void main(String[] args){
        Random r = new Random();

        for(int i = 0; i < 3; i++){
            int number = r.nextInt(10);
            System.out.println("number: " + number);
        }
    }
}

10.2.3 - 练习

// 获取 1 - n 之间的随机数
import java.util.Random

public class Test01_Random{
    public static void main(String[] args){
        int n = 50;

        Random r = new Random();
        int number = r.nextInt(n) + 1;
        System.out.println("number: " + number);
    }
}

// 游戏开始时,会随机生成一个1-100之间的整数 number 。玩家猜测一个数字 guessNumber ,会与 number 作比较,系统提示大了或者小了,直到玩家猜中,游戏结束。

import java.util.Random;
import java.util.Scanner;

public class Test02_Random{
    public static void main(String[] args){
        Random r = new Random();
        int number = r.nextInt(100) + 1;

        while(true){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入你要猜的数字(1-100):");
            int guessNumber = sc.nextInt();
                
            if(guessNumber > number){
                System.out.println("你猜的数据 " + guessNumber + " 大了");
            } else if(guessNumber < number){
                System.out.println("你猜的数据 " + guessNumber + " 小了");
            } else {
                System.out.println("恭喜你,猜中你");
                break;
            }
        }
    }
}

10.3 - ArrayList类

10.3.1 - 引入——对象数组

// 使用学生数据,存储三个学生对象

public class Student{
    private String name;
    private int age;

    public Student(){};
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }        

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }
}

public class Test01_Student{
    public static void main(String[] args){
        Student[] students = new Student[3];

        Student s1 = new Student("曹操",40);
        Student s2 = new Student("刘备",35);
        Student s3 = new Student("孙权",30);

        students[0] = s1;
        students[1] = s2;
        students[2] = s3;

        for(int i = 0; i < students.length; i++){
            Student s  = students[i];
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}

// 数组的长度是固定的,无法适应数据变化的需求
// Java提供了另一个容器 java.util.ArrayList 集合类,让我们可以更便捷的存储和操作对
象数据.

10.3.2 - 什么是ArrayList类

java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素。此类提供一些方法来操作内部存储的元素。 ArrayList 中可不断添加元素,其大小也自动增长。

10.3.3 - ArrayList使用步骤

查看类

// java.util.ArrayList <E> :该类需要 import导入使后使用。

// <E> ,表示一种指定的数据类型,叫做泛型。 E ,取自Element(元素)的首字母。在出现 E 的地方,我们使用一种引用数据类型将其替换即可,表示我们将存储哪种引用类型的元素。

ArrayList<String>,ArrayList<Student>

查看构造方法

// public ArrayList() :构造一个内容为空的集合。

ArrayList<String> list = new ArrayList<String>();

查看成员方法

// public boolean add(E e) : 将指定的元素添加到此集合的尾部。
// 参数 E e ,在构造ArrayList对象时, <E> 指定了什么数据类型,那么 add(E e) 方法中,只能添加什么数据类型的对象。

// 使用ArrayList类,存储三个字符串元素。

public class Test02_Student_ArrayList{
    public static void main(String[] args){
        AraayList<String> list = new ArrayList<String>();

        String s1 = "曹操";
        String s2 = "刘备";
        String s3 = "孙权";

        System.out.println(list);

        list.add(s1);
        list.add(s2);
        lise.add(s3);

        System.out.println(list);
    }
}

10.3.4 - 常用方法和遍历

// 对于元素的操作,基本体现在——增、删、查。常用的方法有:
// public boolean add(E e) :将指定的元素添加到此集合的尾部。
// public E remove(int index) :移除此集合中指定位置上的元素。返回被删除的元素
// public E get(int index) :返回此集合中指定位置上的元素。返回获取的元素
// public int size() :返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界

public class Demo01_ArrayList_Method{
    public static void main(String[] args){
        ArrayList<String> list = new ArrayList<String>();

        list.add("hello");
        list.add("world");
        list.add("java");

        System.out.println("get: " + list.get(0));
        System.out.println("get: " + list.get(1));
        System.out.println("get: " + list.get(2));

        System.out.println("size: " + list.size());

        System.out.println("remove: " + list.remove(0));

        for(int i = 0; i < list.size(); i++){
            System.out.println(list.get(i));
        }
    }
}

10.3.5 - 如何存储基本数据类型

ArrayList对象不能存储基本类型,只能存储引用类型的数据。类似 <int> 不能写,但是存储基本数据类型对应的
包装类型是可以的。所以,想要存储基本类型数据, <> 中的数据类型,必须转换后才能编写。

基本类型基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

只有 Integer 和 Character 需要特殊记忆,其他基本类型只是首字母大写即可。

public class Demo2_ArrayList_Method{
    public static void main(String[] args){
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        System.out.println(list);
    }
}

10.3.6 - ArrayList练习

// 获取所有偶数元素集合的方法

public class Test_ArrayList{
    public static void main(String[] args){
        Random random = new Random();
        ArrayList<Integer> list = new ArrayList<>();

        for(int i = 0; i < 20; i++){
            int r = random.nextInt(1000) + i;
            list.add(r);
        }
        
        ArrayList<Integer> arrayList = getArrayList(list);
        System.out.println(arrayList);
    }

    public static ArrayList<Integer> getArrayList(ArrayList<Integer> list){
        ArrayList<Integer> smallList = new ArrayList<Integer>();

        for(int i = 0; i < list.size(); i++){
            Integer num = list.get(i);
            
            if(num % 2 == 0){
                smallList.add(num);
            }
        }
        
        return smallList;
    }
}

 

11 - String类、static关键字、Arrays类、Math类

11.1 - String类

11.1.1 - 概述及特点

概述

java.lang.String 类代表字符串。Java程序中所有的字符串文字(例如 "abc" )都可以被看作是实现此类的实
例。

类 String 中包括用于检查各个字符串的方法,比如用于比较字符串,搜索字符串,提取子字符串以及创建具有翻
译为大写或小写的所有字符的字符串的副本。

特点

1. 字符串不变:字符串的值在创建后不能被更改。

String s1 = 'abc';
s1 += "d";
System.out.println(s1);    // "abcd"

// 内存中有"abc","abcd"两个对象,s1从指向"abc",改变指向,指向了"abcd" 

2. 因为String对象时不可变的,所以它们可以被共享。

String s1 = "abc";
String s2 = "abc";

// 内存中只有一个"abc"对象被创建,同时被s1和s2共享

3. “abc”等效于"char[] data = {'a','b','c'}"

例如:
String str = "abc";

相当于:
char data[] = {'a','b','c'};
String str = new String(data);
// String底层是靠字符数组实现的

11.1.2 - 使用步骤

查看类:

java.lang.String :此类不需要导入。

查看构造方法:

// public String() :初始化新创建的 String对象,以使其表示空字符序列。
// public String(char[] value) :通过当前参数中的字符数组来构造新的String。
// public String(byte[] bytes) :通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String。

// 无参构造
String str = new String();

// 通过字符数组构造;
String chars[] = {'a','b','c'};
String str2 = new String(chars);

// 通过字节数组构造
byte bytes[] = {97,98,99};
String str3 = new String(bytes);

11.1.3 - 常用方法

判断功能的方法

// public boolean equals(Object anObject)    将此字符串与指定对象进行比较
// public boolean equalsIgnoreCase(String antherString)    将此字符串与指定对象进行比较,忽略大小写

public class String_Demo01{
    public static void main(String[] args){
        String s1 = "hello";
        String s2 = "hello";
        String s3 = "HELLO";

        System.out.println(s1.equals(s2));
        System.out.println(s2.equals(s3));
        System.out.println(" - - - - - - - -")

        System.out.println(s1.equalsIgnoreCase(s2));
        System.out.println(s1.equalsIgnoreCase(s3));
    }
}

获取功能的方法

// public int length () :返回此字符串的长度。
// public String concat (String str) :将指定的字符串连接到该字符串的末尾。
// public char charAt (int index) :返回指定索引处的 char值。
// public int indexOf (String str) :返回指定子字符串第一次出现在该字符串内的索引。
// public String substring (int beginIndex) :返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。
// public String substring (int beginIndex, int endIndex) :返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex

public class String_Demo02 {
    public static void main(String[] args) {
        //创建字符串对象
        String s = "helloworld";

        // int length():获取字符串的长度,其实也就是字符个数
        System.out.println(s.length());
        System.out.println("‐‐‐‐‐‐‐‐");

        // String concat (String str):将将指定的字符串连接到该字符串的末尾.
        String s = "helloworld";
        String s2 = s.concat("**hello itheima");
        System.out.println(s2);        // helloworld**hello itheima

        // char charAt(int index):获取指定索引处的字符
        System.out.println(s.charAt(0));
        System.out.println(s.charAt(1));
        System.out.println("‐‐‐‐‐‐‐‐");

        // int indexOf(String str):获取str在字符串对象中第一次出现的索引,没有返回‐1
        System.out.println(s.indexOf("l"));
        System.out.println(s.indexOf("owo"));
        System.out.println(s.indexOf("ak"));
        System.out.println("‐‐‐‐‐‐‐‐");

        // String substring(int start):从start开始截取字符串到字符串结尾
        System.out.println(s.substring(0));
        System.out.println(s.substring(5));
        System.out.println("‐‐‐‐‐‐‐‐");

        // String substring(int start,int end):从start到end截取字符串。含start,不含end。
        System.out.println(s.substring(0, s.length()));
        System.out.println(s.substring(3,8));
    }
}

转换功能的方法

// public char[] toCharArray () :将此字符串转换为新的字符数组。
// public byte[] getBytes () :使用平台的默认字符集将该 String编码转换为新的字节数组。
// public String replace (CharSequence target, CharSequence replacement) :将与target匹配的字符串使用replacement字符串替换。

public class String_Demo03 {
    public static void main(String[] args) {
        //创建字符串对象
        String s = "abcde";

        // char[] toCharArray():把字符串转换为字符数组
        char[] chs = s.toCharArray();
        for(int x = 0; x < chs.length; x++) {
            System.out.println(chs[x]);
        } 
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐");

        // byte[] getBytes ():把字符串转换为字节数组
        byte[] bytes = s.getBytes();
        for(int x = 0; x < bytes.length; x++) {
            System.out.println(bytes[x]);
        } 
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐");

        // 替换字母it为大写IT
        String str = "itcast itheima";
        String replace = str.replace("it", "IT");
        System.out.println(replace); // ITcast ITheima
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐");
    }
}

分割功能的方法

// public String[] split(String regex) :将此字符串按照给定的regex(规则)拆分为字符串数组

public class String_Demo03 {
    public static void main(String[] args) {
        //创建字符串对象
        String s = "aa|bb|cc";
        String[] strArray = s.split("|"); // ["aa","bb","cc"]
        for(int x = 0; x < strArray.length; x++) {
            System.out.println(strArray[x]); // aa bb cc
        }
    }
}

11.1.4 - 练习

// 定义一个方法,把数组{1,2,3}按照指定个格式拼接成一个字符串。格式参照如下:[word1#word2#word3]。

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

        String s = arrayToString(arr);

        System.out.println("s: " + s);
    }

    public static String arrayToString(int[] arr){
        String s = new String("[");
    
        for(int i = -; i < arr.length; i++){
            if(x == arr.length - 1){
                s = s.concat(arr[i] + "]");
            } else {
                s = s.concat(arr[i] + "#");
            }
        }
        
        return s;
    }
}


// 键盘录入一个字符,统计字符串中大小写字母以及数字字符个数
public class StringTest2{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串数据:");
        String s = sc.nextLine();

        int bigCount = 0;
        int smallCount = 0;
        int numberCount = 0;

        for(int i = 0; i < s.length(); i++){
            char ch = s.charAt(i);
            
            if(ch >= 'A' && ch <= 'Z'){
                bigCount++;  
            } else if(ch >= 'a' && ch <= 'z'){
                smallCount++;
            } else if(ch >= '0' && ch <= '9'){
                numberCount++;
            } else {
                System.out.printInt("该字符 " + ch + " 非法");
            }
        }
        
        System.out.printInt("大写字符:" + bigCount + " 个");
        System.out.printInt("小写字符:" + smallCount + " 个");
        System.out.printInt("数字字符:" + numberCount + " 个");
    }
}

11.2 - static关键字

11.2.1 - 概述

关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属
于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。

11.2.2 - 定义和使用方式

类变量

当static修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。

类变量:使用static关键字修饰的成员变量。

// 格式:
// static 数据类型 变量名;

// 举例:
static int numberID;

静态方法

当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要
创建类的对象。调用方式非常简单。

类方法:使用static关键字修饰的成员方法,习惯称为静态方法。

// 格式:
修饰符 static 返回值类型 方法名(参数列表){
    // 执行语句
}

// 举例:
public static void showNum(){
    System.out.println("num: " + numberOfStudent);
}

静态方法调用的注意事项

  • 静态方法可以直接访问类变量和静态方法
  • 静态方法不能直接访问普通成员变量或者成员方法。反之,成员方法可以直接访问类变量或者静态方法。
  • 静态方法中,不能使用this关键字。
  • 静态方法只能访问静态成员
调用格式

被static修饰的成员可以并且建议通过类名直接访问。虽然也可以通过对象名访问静态成员,原因即多个对象均属
于一个类,共享使用同一个静态成员,但是不建议,会出现警告信息。

// 格式:
// 访问类变量
类名.类变量名;
// 调用静态方法
类名.静态方法名(参数);

// 举例
public class StuDemo2{
    public static void main(String[] args){
        System.out.println(Student.numberOfStudent);
        Student.showNum();
    }
}

11.2.3 - 静态原理图解

static修饰的内容:

  • 是随着类的加载而加载的,且只加载一次。
  • 存储于一块固定的内存区域,所以,可以直接被类名调用。
  • 它优先于对象存在,所以,可以被所有对象共享。

11.2.4 - 静态代码块

静态代码块:定义在成员位置,使用static修饰的代码块。

位置:类中方法外。

执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。

public class ClassName{
    static{
        // 执行语句
    }
}

作用:给类变量进行初始化赋值

public class Game{
    public static int number;
    public static ArrayList<String> list;

    static{
        number = 2;
        list = new ArrayList<String>();
        
        list.add("张三");
        list.add("李四");
    }
}

// static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。下面将介绍两个工具类,来体现static方法的便利。

11.3 - Arrays类

11.3.1 - 概述

java.util.Arrays 此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法,调用起来
非常简单。

11.3.2 - 操作数组的方法

// public static String toString(int[] a) 返回指定数组内容的字符串表示形式

public static void main(String[] args){
    int[] arr = {2,34,35,4,657,8,69,9};

    System.out,println(arr);
    
    String s = Arrays.toString(arr);
    System.out.println(s);    // [2,34,35,4,657,8,69,9]
}

// public static void sort(int[] a) 对指定的int类型数组按数字升序进行排序

public static void main(String[] args){
    int[] arr = {24,7,5,48,4,46,35,11,6,2};
    System.out.println("排序前:" + Arrays.toString(arr));
    Arrays.sort(arr);
    System.out.println("排序后:" + Arrays.toString(arr));
}

11.3.3 - 练习

// 使用 Arrays 相关的API,将一个随机字符串中的所有字符升序排列,并倒序打印。
public class ArraysTest{
    public static void main(String[] args){
        String line = "asdfklasjdhfklasj";
        char[] chars = line.toCharArray();

        Arrays.sort(chars);
        for(int i = chars.length - 1; i >= 0; i++){
            System.out.print(chars[i] + " ");
        }
    }
}

11.4 - Math类

11.4.1 - 概述

java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具
类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。

11.4.2 - 基本运算方法

// public static double abs(double a)    返回double值的绝对值

double d1 = Math.abs(-5);
double d2 = Math.abs(5);


// public static double ceil(double a)    返回大于等于参数的最小整数

double d1 = Math.ceil(-3.3);    // -3.0
double d2 = Math.ceil(5.1);     // 6.0


// public static double floor(double a)    返回小于等于参数最大的整数

double d1 = Math.floor(3.3);    // 3.0
double d2 = Math.floor(-3.3);   // -4.0


// public static long round(double a)    返回最接近参数的long(相当于四舍五入)
long d1 = Math.round(5.5);     // 6.0
loong d2 = Math.round(5.4);    // 5.0

11.4.3 - 练习

// 使用 Math 相关的API,计算在 -10.8 到 5.9 之间,绝对值大于 6 或者小于 2.1 的整数有多少个

public class MathTest{
    public static void main(String[] args){
        double min = -10.8;
        double max = 5.9;
        int count = 0;

        for(double i = Math.ceil(min); i <= max; i++){
            if(Math.abs(i) > 6 || Math.abs(i) < 2.1){
                count++;
            }            
        }
        System.out.println("个数为:" + count + " 个");
    }
}

 

12 - 继承、抽象类

12.1 - 继承

12.1.1 - 概述

由来

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要
继承那一个类即可。

定义

继承:就是子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接
访问父类中的非私有的属性和行为

好处:

  1. 提高了代码的复用性

  2. 类与类之间产生了关系,是多态的前提

12.1.2 - 继承的格式

通过 extends 关键字,可以声明一个子类继承另外一个父类

class 父类{
    ...
}

class 子类 extends 父类{
    ...
}


// 举例

class Employee{
    String name;

    public void work(){
        System.out.println("工作");
    }
}

class Teacher extends Employee{
    public void printName(){
        System.out.println("name= " + name);
    }
}

public class ExtendDemo01{
    public static void main(String[] args){
        Teacher t = new Teacher();

        t.name = "小明";

        t.printName();

        t.work();
    }
}

12.1.3 - 继承后的特点 —— 成员变量

成员变量不重名

如果子类父类中出现不重名的成员变量,这时的访问时没有影响的。

class Fu{
    int num = 5;
}

class Zi extends Fu{
    int num2 = 6;
   
    public void show(){
        System.out.println("Fu num = " + num);
        System.out.println("Zi num2 = " + num2);
    }
}

class ExtendDemo2{
    public static void main(String[] args){
        Zi z = new Zi();
        z.show();
    }
}

// Fu num = 5
// Zi num2 = 6

成员变量重名

如果子类父类中出现重名的成员变量,这时的访问是有影响的。

class Fu{
    int num = 5;
}

class Zi extends Fu{
    int num = 6;
   
    public void show(){
        System.out.println("Fu num = " + num);
        System.out.println("Zi num = " + num2);
    }
}

class ExtendDemo2{
    public static void main(String[] args){
        Zi z = new Zi();
        z.show();
    }
}

// Fu num = 6
// Zi num = 6

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰
父类成员变量,类似于之前学过的 this。

// 格式
super.父类成员变量名

// 子类方法需要修改
class Zi extends Fu{
    int num = 6;
    
    public void show(){
        System.out.println("Fu num = " + super.num);
        System.out.println("Zi num = " + this.num);
    }
}

// Fu num = 5
// Zi num = 6


// Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能
直接访问的。
// 通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。

12.1.4 - 继承后的特点 —— 成员方法

成员方法不重名

如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对
应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

class Fu{
    public void show(){
        System.out.println("Fu类中的show方法执行");
    }
} 

class Zi extends Fu{
    public void show2(){
        System.out.println("Zi类中的show2方法执行");
    }
} 

public class ExtendsDemo04{
    public static void main(String[] args) {
        Zi z = new Zi();
        //子类中没有show方法,但是可以找到父类方法去执行
        z.show();
        z.show2();
    }
}

成员方法重名——重写(Override)

如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)

方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效
果,也称为重写或者复写。声明不变,重新实现

class Fu {
    public void show() {
        System.out.println("Fu show");
    }
} 

class Zi extends Fu {
    //子类重写了父类的show方法
    public void show() {
        System.out.println("Zi show");
    }
} 

public class ExtendsDemo05{
    public static void main(String[] args) {
        Zi z = new Zi();
        // 子类中有show方法,只执行重写后的show方法
        z.show(); // Zi show
    }
}

重写的应用

子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从
而进行扩展增强。

class Phone {
    public void sendMessage(){
        System.out.println("发短信");
    } 
    public void call(){
        System.out.println("打电话");
    } 
    public void showNum(){
        System.out.println("来电显示号码");
    }
}

//智能手机类
class NewPhone extends Phone {
    //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
    public void showNum(){
        //调用父类已经存在的功能使用super
        super.showNum();
        //增加自己特有显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");
    }
} 

public class ExtendsDemo06 {
    public static void main(String[] args) {
        // 创建子类对象
        NewPhone np = new NewPhone();
        // 调用父类继承而来的方法
        np.call();
        // 调用子类重写的方法
        np.showNum();
    }
}

// 这里重写时,用到super.父类成员方法,表示调用父类的成员方法。

注意事项

  1. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
  2. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。

12.1.5 - 继承后的特点 —— 构造方法

  1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构
    造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
class Fu{
    private int n;

    Fu(){
        System.out.println("Fu()");
    }
}

class Zi extends Fu{
    Zi(){
        // super() 调用父类构造方法
        super();
        System.out.println("Zi()");
    }
}

public class ExtendsDemo07{
    public static void main(String[] args){
        Zi zi = new Zi();
    }
}

// Fu()
// Zi()

12.1.6 - super 和 this

父类空间优先于子类对象产生

在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空
间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构
造方法调用时,一定先调用父类的构造方法。

super和this的含义

  • super    代表父类的存储空间标识(可以理解为父类的引用)

  • this        代表当前对象的引用(谁调用就代表谁)

super和this的用法

// 访问成员
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的

this.成员方法名() ‐‐ 本类的
super.成员方法名() ‐‐ 父类的

// 用法演示
class Animal {
    public void eat() {
        System.out.println("animal : eat");
    }
} 

class Cat extends Animal {
    public void eat() {
        System.out.println("cat : eat");
    } 
    public void eatTest() {
        this.eat(); // this 调用本类的方法
        super.eat(); // super 调用父类的方法
    }
} 

public class ExtendsDemo08 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.eat();
        Cat c = new Cat();
        c.eatTest();
    }
} 

// animal : eat
// cat : eat
// animal : eat


// 访问构造方法
this(...) ‐‐ 本类的构造方法
super(...) ‐‐ 父类的构造方法

// 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
// super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

12.1.7 - 继承的特点

// java只支持单继承,不支持多继承
// 一个类只能有一个父类,不可以有多个父类
class C extends A{}     // ok
class C extends A,B...  // error

// java支持多层继承(继承体系)
// 顶层父类时Object类,所有的类默认继承Ojbect,作为父类
class A{}
class B extends A{}
class C extends B{}

// 父类和子类是一种相对的概念。

12.2 - 抽象类

12.2.1 - 概述

父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有
意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法
的类就是抽象类

抽象方法 : 没有方法体的方法。

抽象类:包含抽象方法的类。

12.2.2 - abstract使用格式

抽象方法

使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

// 格式
修饰符 abstract 返回值类型 方法名(参数列表);

// 举例
public abstract void run();

抽象类

如果一个类包含抽象方法,那么该类必须是抽象类。

// 格式
abstract class 类名字{
    ...
}

// 举例
public abstract class Animal{
    public abstract void run();
}

抽象的使用

继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父
类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

public class Cat extends Animal {
    public void run (){
        System.out.println("小猫在墙头走~~~");
    }
} 

public class CatTest {
    public static void main(String[] args) {
        // 创建子类对象
        Cat c = new Cat();
        // 调用run方法
        c.run();
    }
} 

// 输出结果:
// 小猫在墙头走~~~


// 此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

12.2.3 - 注意事项

关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设
计。

4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有
意义。

12.2.4 - 综合案例

// 群主发普通红包。某群有多名成员,群主给成员发普通红包。普通红包的规则:
// 1. 群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取。
// 2. 成员领取红包后,保存到成员余额中。
// 请根据描述,完成案例中所有类的定义以及指定类之间的继承关系,并完成发红包的操作。


// 用户类
public class User{
    private String username;
    private double leftMoney;

    public User(){}
    public User(String username, double leftMoney){
        this.username = username;
        this.leftMoney = leftMoney;
    }

    public void setUsername(String username){
        this.username = username;
    }

    public String getUseranme(){
        return username;
    }

    public void setLeftMoney(double leftMoney){
        this.leftMoney = leftMoney
    }

    public double getLeftMoney(){
        return leftMoney;
    }
    
    public void show(){
        System.out.println("用户名: " + username " ,余额为:" + leftMoney + " 元");
    }
}


// 定义群主类
public class QunZhu extends User{
    public QunZhu(){}
    public QunZhu(String username, double leftMoney){
        super(useranme, leftMoney);
    }

    public ArrayList<Double> send(int money, int count){
        double leftMoney = getLeftMoney();
        
        if(money > leftMoney){
            return null;
        }

        setLeftMoney(leftMoney - money);

        ArrayList<Double> list = new ArrayList<>();

        money = money * 100;

        int m = money / count;
        int l = money % count;

        for(int i = 0; i < count - 1; i++){
            list.add(m / 100.0);
        }

        if(l == 0){
            list.add(m / 100.0);
        } else {
            list.add((m + 1) / 100.0);
        }
    
        return list;
    }
}


// 成员类
public class Member extends User{
    public Member(){}
    public Member(String username, double leftMoney){
        super(username,leftMoney);
    }

    public void openHongbao(ArrayList<Double> list){
        Random r = new Random();
        int index = r.nextInt(list.size());
        Double money = list.remove(index);
        setLeftMoney(money);
    }
}


// 测试类
public class Test{
    public static void main(String[] args){
        QunZhu qz = new QunZhu("群主", 200);

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入金额:");
        int money = sc.nextInt();
        System.out.println("请输入个数:");
        int count = sc.nextInt();

        ArrayList<Double> sendList = s.send(money,count);

        if(sendList == null){
            System.out.println("余额不足...");
            return;
        }

        Member m = new Member();
        Member m2 = new Member();
        Member m3 = new Member();

        m.openHongbao(sendList);
        m2.openHongbao(sendList);
        m3.openHongbao(sendList);

        qz.show();
        m.show();
        m2.show();
        m3.show();
    }
}

 

13 - 接口、多态

13.1 - 接口

13.1.1 - 概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么
接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法
(JDK 9)。

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并
不是类,而是另外一种引用数据类型。

引用数据类型:数组,类,接口。

接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做
是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象
类。

13.1.2 - 定义格式

public interface 接口名称{
    // 抽象方法
    // 默认方法
    // 静态方法
    // 私有方法
}

含有抽象方法

// 抽象方法:使用abstract关键字修饰,可以省略,没有方法体,该方法体子类实现使用。

public interface InterfaceName(){
    public abstract void method();
}

含有默认方法和静态方法

// 默认方法:使用default修饰,不可省略,供子类调用或者子类重写。
// 静态方法:使用static修饰,供接口直接调用。

public interface InterfaceName{
    public default void method{
        // 执行语句
    }
    public static void method2{
        // 执行语句
    }
}

含有私有方法和私有静态方法

// 私有方法:使用private修饰,供接口中的默认方法或者静态方法调用

public interface InterfaceName{
    private void method(){
        // 执行语句
    }
}

13.1.3 - 基本的实现

实现的概述:

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类
似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。

非抽象子类实现接口:

1.必须重写接口中所有的抽象方法。

2.继承了接口的默认方法,即可以直接调用,也可以重写。

class 类名 implements 接口名{
    // 重写接口中抽象方法【必须】
    // 重写接口中默认方法【可选】
}

抽象方法的使用:

// 定义接口
public interface LiveAble{
    // 定义抽象方法
    public abstract void eat();
    public abstract void sleep();
}

// 定义实现类
public class Animal implements LiveAble{
    @Override
    public void eat(){
        System.out.println("吃东西");
    }

    @Override
    public void sleep(){
        System.out.println("晚上睡");
    }
}

// 测试类
public class InterfaceDemo{
    public static void main(String[] args){
        // 创建子类对象
        Animal a = new Animal();
        // 调用实现后的方法
        a.eat();
        a.sleep();        
    }
}

// 输出结果:
// 吃东西
// 晚上睡

默认方法的使用:

/****************
*
* 1. 继承默认方法
*
*****************/

// 定义接口
public interface LiveAble{
    public default void fly(){
        System.out.println("天上飞");
    }
}

// 定义实现类
public class Animal implements LiveAble{
    // 继承,什么都不用写,直接调用
}

// 定义测试类
public class InterfaceDemo{
    public static void main(String[] args){
        //创建子类对象
        Animal a = new Animal();
        // 调用默认方法
        a.fly();
    }
}

// 输出结果
// 天上飞



/****************
*
* 2.重写默认方法
*
*****************/

// 定义接口
public interface LiveAble{
    public default void fly(){
        System.out.println("天上飞");
    }
}

// 定义实现类
public class Animal implements LiveAble{
    @Override
    public void fly(){
        System.out.println("自由自在的飞");
    }
}

// 定义测试类
public class InterfaceDemo{
    public static void main(String[] args){
        //创建子类对象
        Animal a = new Animal();
        // 调用默认方法
        a.fly();
    }
}

// 输出结果
// 自由自在的飞

静态方法的使用:

// 静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用

// 定义接口
public interface LivaAble{
    public static void run(){
        System.out.println("跑起来");
    }
}


// 定义实现类
public class Animal implements LiveAble{
    // 无法重写静态方法
}

// 定义测试类
public class InterfaceDemo{
    public static void main(String[] args){
        // Animal.run();   【错误】 无法继承方法,也无法调用
        LiveAble.run();
    }
}

// 输出结果:
// 跑起来

私有方法的使用:

  • 私有方法:只有默认方法可以调用。
  • 静态方法:默认方法和静态方法可以调用。

如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法
去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。同学们在已学技术的基础上,可以自行测
试。

public interface LiveAble{
    default void func(){
        func1();
        func2();
    }
    
    private void func1(){
        System.out.println("跑起来");
    }

    private void func2(){
        System.out.println("跑起来");
    }
}

13.1.4 - 接口的多实现 

在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接
口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

class 类名[extends 父类名] implements 接口名1,接口名2,接口名3... {
    // 重写接口中抽象方法【必须】
    // 重写接口中默认方法【不重名时可选】
}

抽象方法

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。

// 定义多个接口
interface A{
    public abstract void showA();
    public abstract void show();
}

interface B{
    public abstract void showB{};
    public abstract void show();
}

// 定义实现类
public class C implements A,B{
    @Override
    public void showA(){
        System.out.println("showA");
    }

    @Override
    public void showB(){
        System.out.println("showB");
    }

    @Override
    public void show(){
        System.out.println("show");
    }
}

默认方法

接口中,有多个默认方法时,实现类都可以继承使用,如果默认方法有重名的,必须重写一次。

// 定义多个接口
interface A{
    public default void methodA(){}
    public default void method(){}
}

interface B{
    public default void methodB(){}
    public default void method(){}
}

// 定义实现类
public class C implements A,B{
    @Override
    public void method(){
        System.out.println("method");
    }
}

静态方法:

接口中,存在同名的静态方法并不会冲突,原因是只能通过各自的接口名访问静态方法。

优先级的问题

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。

// 定义接口
interface A{
    public default void methodA(){
        System.out.println("AAAAAAA");
    }
}

// 定义父类
class D{
    public void methodA(){
        System.out.println("DDDDDDDDD");
    }
}

// 定义子类
class C extends D implements A{
    // 未重写方法
}

// 测试类
public class Test{
    public static void main(String[] args){
        C c = new C();
        C.methodA();
    }
}

// 输出结果
// DDDDDDDDD

13.1.5 - 接口的多继承

一个接口能够继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次

// 父接口
interface A{
    public default void method(){
        System.out.println("AAAAA");
    }
}

interface B{
    public default void method(){
        System.out.println("BBBBB");
    }
}

// 子接口
interface D extends A,B{
    @Override
    public default void method(){
        System.out.println("DDDDD");
    }
}

// 子接口重写默认方法时,default关键字可以保留
// 子类重写默认方法时,default关键字不可以保留

13.1.6 - 其他成员特点

  • 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
  • 接口中,没有构造方法,不能创建对象。
  • 接口中,没有静态代码块。

13.2 - 多态

13.2.1 - 概述

多态是继封装、继承之后,面向对象的第三大特性。

多态:是指同一行为,具有多个不同的表现形式。

继承或者实现【二选一】,方法的重写【意义体现:不重写,无意义】,父类引用指向子类对象【格式体现】

13.2.2 - 多态的体现

// 格式
// 父类类型:指子类对象继承的父类类型,或者实现的父接口类型
父类类型 变量名 = new 子类对象;
变量名.方法名();


// 父类类型:子类对象继承的父类类型,或者实现的父接口类型。
Fu f = new Zi();
f.method();


// 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的时子类重写后方法。


// 定义父类
public abstract class Ainmal{
    public abstract void eat();
}

// 定义子类
class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("吃骨头");
    }
}

// 定义测试类
public class Test{
    public static void main(String[] args){
        // 多态形式,创建对象
        Animal a1 = new Cat();
        // 调用的时 Cat 的 eat
        
        // 多态形式,创建对象
        Animal a2 = new Dog();
        // 调用的时 Dog 的 eat
        a2.eat(); 
    }
}

13.2.3 - 多态的好处

实际开发过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展与便利。

// 定义父类
public abstract class Animal{
    public abstract void eat();
}


// 定义子类
class Cat extends Animal{
    public void eat(){
        System.out,println("吃鱼");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("吃骨头");
    }
}


// 定义测试类
public class Test{
    public static void main(String[] args){
        // 多态形式,创建对象
        Cat c = new Cat();
        Dog d = new Dog();

        // 调用showCatEat
        showCatEat(c);

        // 调用showDogEat
        showDogEat(d);

        /*
            以上两个方法,均可以被showAnimalEat(Animal a)方法所代替
            执行效果一致
        */

        showAnimalEat(c);
        showAnimalEat(d);
    }

    public static void ShowCatEat(Cat c){
        c.eat();
    }

    public static void ShowDogEat(Dog d){
        d.eat();
    }

    public static void showAinmalEat(Animal a){
        a.eat();
    }
}

由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当
然可以把Cat对象和Dog对象,传递给方法。

当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,
所以showAnimalEat完全可以替代以上两方法。

不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用
showAnimalEat都可以完成。

所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。

13.2.4 - 引用类型转换

多态的转型分为向上转型和向下转型两种:

向上转型:

多态本身时子类类型向父类类型向上转换的过程,这个过程是默认的。

当父类引用指向一个子类对象时,便是向上转型。

父类类型 变量名= new 子类类型();

Aimal a = new Cat();

向下转型:

父类类型向子类类型向下转换得到过程,这个过程是强制的。

一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

子类类型 变量名 = (子类类型)父类变量名;

Cat c = (Cat) a;

为什么要转型:

当使用多态方式调用方式时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别提运行了。这也是多态给我们带来的一点“小麻烦”。所以,想要调用子类特有的方法,必须向下转型。

// 定义类
abstract class Animal{
    abstract void eat();
}

class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼");
    }
    public void catchMouse(){
        System.out.pritnln("抓老鼠");
    }
}

class Dog extends Animal(){
    public void eat(){
        System.out.println("吃骨头");
    }
    public void watchHouse(){
        System.out.println("看家");
    }
}

// 定义测试类
public class Test{
    public static void main(String[] args){
        // 向上转型
        Animal a = new Cat();
        a.eat();                // 调用的是 Cat 的 eat

        // 向下转型
        Cat c = (Cat) a;
        c.catchMouse();         // 调用的是 Cat 的 catchMouse
    }
}

转型的异常:

public class Test {
    public static void main(String[] args) {
        // 向上转型
        Animal a = new Cat();
        a.eat(); // 调用的是 Cat 的 eat

        // 向下转型
        Dog d = (Dog)a;
        d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
    }
}

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了
Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验。

// 变量名 instanceof 数据类型
// 如果变量属于该数据类型,返回true
// 如果变量不属于该数据类型,返回false


public class Test(){
    public static void main(String[] args){
        // 向上转型
        Aniaml a = new Cat();
        
        // 向下转型
        if(a instanceof Cat){
            Cat c = (Cat)a;
            c.catchMouse();
        } else if(a instanceof Dog){
            Dog d = (Dog)a;
            d.watchHouse();
        }
    }
}

13.3 - 接口多态的综合案例

// USB接口
interface USB{
    void open();    // 开启功能
    void close();   // 关闭功能
}

// 鼠标类
class Mouse implements USB{
    public void open(){
        System.out.println("鼠标开启,红灯亮");
    }

    public void close(){
        System.out.println("鼠标关闭,红灯熄灭");
    }

    public void click(){
        System.out.println("鼠标单击");
    }
}

// 键盘类
class KeyBorad implements USB{
    public void open(){
        System.out.println("键盘开启,绿灯亮");
    }

    public void close(){
        System.out.println("键盘关闭,绿灯灭");
    }

    public void type(){
        System.out.println("键盘打字");
    }
}

class Laptop{
    // 笔记本开启功能
    public void run(){
        System.out.println("笔记本运行");
    }

    // 笔记本使用USB设备,这时当笔记本对象调用这个功能时,必须给其传递一个符合USB规则的USB设备
    public void useUSB(USB usb){
        if(usb != null){
            usb.open();
            // 类型转换,调用特有方法
            if(usb instanceof Mouse){
                Mouse m = (Mouse)usb;
                m.click();
            } else if(usb instanceof KeyBorad){
                KeyBorad kb = (KeyBorad)usb;
                kb.type();
            }
            usb.close();
        }
    }

    public void shutDown(){
        System.out.println("笔记本关闭");
    }
}

public class Test{
    public static void main(String[] args){
        // 创建笔记本对象
        Laptop lt = new Laptop();
        // 笔记本开启
        lt.run();

        // 创建鼠标实体对象
        USB u = new Mouse();
        // 笔记本使用鼠标
        lt.useUSB(u);

        // 创建键盘实体对象
        KeyBorad kb = new KeyBorad();
        // 笔记本使用键盘
        lt.useUSB(kb);

        // 笔记本关闭
        lt.shutDown();
    }
}

 

14 - final、权限、内部类、引用类型

14.1 - final关键字

14.1.1 - 概述

子类可以在父类的基础上改写父类内容,为了避免随意的继承API中提供的类,改写其内容,java提供了关键字final,用于修饰不可改变内容。

final:不可改变,可以用于修饰类、方法和变量。被修饰的类,不能被继承。被修饰的方法,不能被重写。被修饰的变量,不能被重新赋值。

14.1.2 - 使用方式

// 修饰类
final class 类名{

}


// 修饰方法
修饰符 final 返回值类型 方法名(参数列表){
    // 方法体
}


// 修饰变量    1.局部变量——基本类型
public class FinalDemo1{
    public static void main(String[] args){
        final in a;
        a = 10;
        a = 20;    // 第二次赋值,报错,不可重新赋值
    }
}

// 修饰变量    2.局部变量——引用类型
// 引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改,但是不影响对象内部的成员变量值的修改
public class FinalDemo2{
    public static void main(String[] args){
        // 创建User对象
        final User u = new User();
        // 创建 另一个User对象
        u = new User();       // 报错,指向了新的对象,地址值该百年
        // 调用setName方法
        u.setName("张三");    // 可以修改
    }
}


// 成员变量    成员变量涉及到初始化的问题,初始化方式有两种,只能二选一
// 1.显示初始化
public class User{
    final String USERNAME="张三";
    private int age;
}

// 2.构造方法初始化
public class User{
    final String USERNAME;
    private int age;
    public User(String username, int age){
        this.USERNAME = username;
        this.age = age;
    }
}

14.2 - 权限修饰符

14.2.1 - 概述

在java中提供了四种访问权限,使用不同的访问修饰符时,被修饰的内容会有不同的访问权限。

  • public:公共的。
  • protected:受保护的。
  • default:默认的。
  • private:私有的。

14.2.2 - 不同权限的访问能力

 publicprotecteddefaultprivate
同一类中
同一包中(子类与无关类) 
不同包的子类  
不同包中的无关类   

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private,隐藏细节。
  • 构造方法使用public,方便创建对象。
  • 成员方法使用public,方便调用方法。
  • 不加权限修饰符,其访问能力与default修饰符相同。

14.3 - 内部类

14.3.1 - 概述

将类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。

// 成员内部类:定义在类中方法外的类。
class 外部类{
    class 内部类{
    
    }
}

在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。

访问特点:

内部类可以直接访问外部类的成员,包括私有成员。

外部类药访问内部类的成员,必须药创建内部类的对象。

// 创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();


// 定义类
public class Person{
    private boolean live = true;
    
    clas Heart{
        public void jump(){
            // 直接访问外部类成员
            if(live){
                System.out.println("心脏在跳动");
            } else {
                System.out.println("心脏不跳了");
            }
        }
    }
    
    public boolean isLive(){
        return live;
    }

    public void setLive(boolean live){
        this.live = live;
    }
}

public class InnerDemo(){
    public static void main(String[] args){
        Person p = new Person();
        Heart heart = p.new.Heart();
        
        heart.jump();
        p.setLive(false);
        heart.jump();
    }
}

// 输出结果:
// 心脏在跳动
// 心脏不跳了


// 内部类仍然时一个独立的类,在编译后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号

14.3.2 - 匿名内部类

匿名内部类时内部类的简化写法。它的本质时一个【带具体实现的】【父类或者父接口的】【匿名的】子类对象。

开发中,最常用的内部类就是匿名内部类。以接口举例,当你使用一个接口时,似乎需要做如下几步操作:

  1. 定义子类
  2.  接口中的方法
  3. 创建子类对象
  4. 调用重写后的方法

我们的目的,最终只是为了调用方法,匿名内部类就是这样做的快捷方式,将上面的四步合成一步。

匿名内部类必须继承一个父类或者实现一个父接口。

// 格式
new 父类名或者接口名(){
    // 方法重写
    @Override
    public void method(){
        // 执行语句
    }
};


// 定义接口
public abstract class FlyAble{
    public abstract void fly();
}

// 创建匿名内部类,并调用:
public class InnerDemo{
    public static void main(String[] args){
        /*
        1. 等号右边:匿名内部类,定义并创建该接口的子类对象
        2. 等号左边:多态赋值,接口类型引用指向子类对象
        */

        FlyAble f = new FlyAble(){
            public void fly(){
                System.out.println("起飞");
            }
        };

        // 调用fly方法,执行重写后的方法
        f.fly();
    }
}


// 通常在方法的形式参数时接口或者抽象类时,也可以将匿名内部类作为参数传递
public class InnerDemo2{
    public static void main(String[] args){
        FlyAble f = new FlyAble(){
            public void fly(){
                System.out.println("起飞");
            }
        };

        showFly(f);
    }

    public static void showFly(FlyAble f){
        f.fly();
    }
}


// 以上的两步也可以简化为一步
public class InnerDemo3(){
    public stativ void main(String[] args){
        showFly(FlyAble f = new Flyable(){
           public void fly(){
                System.out.println("起飞");
            } 
        });
    }

    public static void showFly(FlyAble f){
        f.fly();
    }
}

14.4 - 引用类型用法总结

14.4.1 - class作为成员变量

class Weapon{
    String name;    // 武器名称
    int hurt;       // 伤害值

    Weapon(String name, int hurt){
        this.name = name;
        this.hurt = hurt;
    }

    public String getName(){
        return name;
    }

    public int getHurt(){
        return hurt;
    }
}

class Armour{
    String name;    // 装备名称
    int protect;    // 防御值

    Armour(String name, int protect){
        this.name = name;
        this.protect = protect;
    }

    public String getName(){
        return name;
    }

    public int getProtect(){
        return protect;
    }
}

class Role{
    int id;
    int blood;
    String name;

    // 添加武器属性
    Weapon wp;
    // 添加盔甲属性
    Armour ar;

    // 提供get/set方法
    public Weapon getWp(){
        return wp;
    }

    public void setWeapon(Weapon wp){
        this.wp = wp;
    }

    public Armour getArmour(){
        return ar;
    }

    public void setArmour(Armour ar){
        this.ar = ar;
    }

    // 攻击方法
    public void attack(){
        System.out.println("使用" + wp.getName() + ",造成" + wp.getHurt() + "点伤害");
    }

    // 穿戴盔甲
    public void wear(){
        this.blood += ar.getProtect();
        System.out.println("穿上" + ar.getName() + ", 生命值增加" + ar.getProtect());
    }
}

public class Test{
    public static void main(String[] args){
        // 创建Weapon 对象
        Weapon wp = new Weapon("屠龙刀" , 999999);
        // 创建Armour 对象
        Armour ar = new Armour("麒麟甲",10000);
        // 创建Role 对象
        Role r = new Role();
        // 设置武器属性
        r.setWeapon(wp);
        // 设置盔甲属性
        r.setArmour(ar);
        // 攻击
        r.attack();
        // 穿戴盔甲
        r.wear();
    }
}


// 类作为成员变量时,对它进行赋值的操作,实际上,时赋给它该类的一个对象。

14.4.2 - interface作为成员变量

interface FaShuSkill{
    public abstract void faShuAttack();
}

class Role{
    FaShuSkill fs;

    public void setFaShuSkill(FaShuSkill fs){
        this.fs = fs;
    }

    // 法术攻击
    public void faShuSkillAttack(){
        System.out.print("发动法术攻击:");
        fs.faShuAttack();
        System.out.println("攻击完毕");
    }
}

public class Test{
    public stativ void main(String[] args){
        Role role = new Role();
        role.setFaShuSkill(new FaShuSkill(){
            @Override
            public void faShuAttack(){
                System.out.println("纵横天下");
            }
        });

        // 发动法术攻击
        role.faShuSkillAttack();

        // 更换技能
        role.setFaShuSkill(new FaShuSkill(){
            @Override
            public void faShuAttack(){
                System.out.println("逆转乾坤");
            }
        });
        
        // 发动法术攻击
        role.faShuSkillAttack();
    }
}


// 使用一个接口,作为成员变量,以便随时能更换技能,这样的设计更加灵活,增加了程序的扩展性
// 接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。

14.4.3 - interface作为方法参数和返回值

当接口作为方法参数,或者是返回值类型时,需要传递的或者返回的都是它的子类对象。

// 获取某集合中所有的偶数

public static List<Integer> getEvenNum(List<Integer> list) {
    // 创建保存偶数的集合
    ArrayList<Integer> evenList = new ArrayList<>();
    // 遍历集合list,判断元素为偶数,就添加到evenList中
    for (int i = 0; i < list.size(); i++) {
        Integer integer = list.get(i);
        if (integer % 2 == 0) {
            evenList.add(integer);
        }
    } 
    /* 
        返回偶数集合
        因为getEvenNum方法的返回值类型是List,而ArrayList是List的子类,
        所以evenList可以返回
    */
    return evenList;
}

public class Test {
    public static void main(String[] args) {
        // 创建ArrayList集合,并添加数字
        ArrayList<Integer> srcList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            srcList.add(i);
        } 

        /* 
            获取偶数集合
            因为getEvenNum方法的参数是List,而ArrayList是List的子类,
            所以srcList可以传递
        */

        List list = getEvenNum(srcList);
        System.out.println(list);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值