javaSe总结1,javase总结
javaSe总结1,javase总结
————基础班————
1.安装 JDK 与配置环境变量
如何配置 :
–创建新的变量名称:JAVA_HOME
计算机-右键属性-高级系统设置-高级-环境变量-系统变量
–为JAVA_HOME添加变量值:JDK安装目录
–在path环境变量最前面添加如下内容
%JAVA_HOME%\bin;
2.第一个 Java 程序_HelloWorld
/*
1. 所有的Java代码都需要写在 类
中. 类必须使用 class 开头.
2. 需要给该类取一个名字. 名字必须遵守 驼峰命名规则
. 类名要求第一个单词首字母要大写, 其后每一个单词首字母都大写.
3. 类名之后书写一对 {}
大括号, 表示该类的作用范围.
4. 如果一个程序需要被运行, 那么该程序就必须拥有一个 主方法
.
主方法的格式 : public static void main(String[] args) {} 主方法就是程序的入口.
5. 我们希望程序能够看到一些结果, 因此需要 输出语句
.
输出语句 : System.out.println(“需要输出的内容即可.”);
6. 源代码书写完成, 之后 保存
, ctr+s 文件类型为: .java 文件名: 必须与类名完全相同(大小写都要一致)
7. 需要对源文件进行 编译
. 指令: javac HelloWorld.java
8. 程序编译成功后, 就可以执行了. 指令: java HelloWorld
*/
class HelloWorld {
public static void main(String[] args) {
System.out.println(“Hello World.”);
}
}
3.注释
/*
注释 : 对程序进行解释和说明的文字.
作用 : 注释是 `程序猿 / 程序媛` 之间的交流工具.
注意 : 注释是不会被虚拟机编译的.
注释的分类 :
1. 单行注释
2. 多行注释 (不能嵌套)
3. 文档注释
说明 : 注释一定要有含义, 不要乱书写, 在编写的代码一定要养成书写注释的习惯.
*/
/**
* 文档注释 : 后期可以将文档注释生成网页文件.
*
* @version 1.0
* @author 传智播客
* @see www.itcast.cn
*/
// 定义了一个类, 类名为 HelloWorld3
class HelloWorld3 {
// 定义了程序的主方法
public static void main(String[] args) {
// 以下的代码只有上帝和我能看懂.
// 现在只有上帝能看懂了.
// 神奇, 勿动.
System.out.println("Hello World, 你好, 世界.");
}
}
4.常量
/*
Java 中的常量 : (固定内容的书写格式)
1. 整型常量 10, 20, 30,
2. 浮点型常量
2.1 单精度浮点型 12345.123456789f; (最多精确到 8 位) 12345.123
2.2 双精度浮点型 12345.123456789; (最多精确到 16 位) 12345.123456789
3. 字符常量 A 中 特殊转义字符 : \ (反斜杠) 必须使用 `单引号` 括起来.
t 字符t \t 制表符 4空格 8空格
n 字符n \n 换行
4. 字符串常量 我们是伟大的中国人. 必须使用 `双引号` 括起来.
5. 布尔类型常量 true 真 (正确) false 假(错误)
6. 空类型常量 null 空 (将一个对象的引用清空)
注意点 :
字符 :
''; 错误! 什么都不写不正确.
' '; 正确! 因为空格也是一个字符.
字符串 :
""; 正确! 字符串可以什么都不写.
" "; 正确! 就写一个空格也正确.
*/
class HelloWorld5
{
public static void main(String[] args)
{
/*
10; // ; 表示一条语句的结束
100;
998;
10.88f;
12345.123456789;
'A';
'中';
'\t';
'\n';
"我们是伟大的中国人.";
true; // 布尔类型
false;
"true"; // 字符串类型
"false";
null; // 空类型
"null"; // 字符串类型
*/
// println -> print line 打印换行
// \t 制表符 (对齐)
System.out.println("hello\nworld");
System.out.println("ab edf");
System.out.println("中国 伟大");
System.out.println("--------------------");
System.out.println("hello\tworld");
System.out.println("ab\tedf");
System.out.println("中国\t伟大");
}
}
5.变量
1.八种基本数据类型的演示 :
/*
基本数据类型 :
*/
class HelloWorld7
{
public static void main(String[] args)
{
// 1. byte -128 ~ 127
byte b = 127;
// 2. short -32768~32767
short s = 32767;
// 3. int -2147483648~2147483647 (默认类型)
int i = 2147483647;
// 4. long -9223372036854775808 ~ 9223372036854775807
long l = 9223372036854775807L;
// 5. float
float f = 12345.123456789f;
// 6. double
double d = 12345.12345678988;
// 7. char
char ch = 'A';
// 8. boolean
System.out.println("ch = " + ch); // 在字符串中, + 符号表示拼接
}
}
2.变量的使用三步曲 :
1.定义 (申请空间) 2. 初始化 (赋值) 3. 使用 (输出, 判断, 赋值 …)
/*
变量使用的三步曲 :
1. 定义
2. 初始化
3. 使用
注意1 : 变量没有初始化, 不可以直接使用.
注意2 : 变量可以在定义的同时进行初始化.
*/
class HelloWorld8
{
public static void main(String[] args)
{
// 1. 定义 (申请空间)
// int i;
// 2. 初始化 (给空间赋值)
// i = 100;
// i = 998;
// 定义变量的同时进行初始化.
int i = 100;
// 3. 使用 (输出, 赋值, 比较, 判断 ... )
System.out.println("i = " + i);
}
}
3.变量的作用域
/*
变量的作用域 : (作用范围)
*/
class HelloWorld9
{
public static void main(String[] args)
{
int num1 = 100;
// 定义了一个作用域 (小的作用范围)
{
int num2 = 998;
System.out.println("num2 = " + num2);
System.out.println("num1 = " + num1);
}
System.out.println("num1 = " + num1);
// 重新申请空间, 再赋值
int num2 = 666;
// int num2 = 888; 错误! 在同一个作用域中, 变量不可以重名.
System.out.println("num2 = " + num2);
}
}
4.变量的数据类型转换 :
/*
变量的数据类型转换 :
特点 : Java语言是 `强类型` 语法. 不同的数据类型之间不可以进行运算.
1. 自动类型转换 : 程序运行时, 不同的数据会以大类型作为参数. 运算时程序就会自动完成类型提升.
2. 强制类型转换 : 当大类型强制赋值给小类型时, 需要手动完成强制类型转换.
错误! 可能损失精度.
1. int -> byte
2. float -> int
*/
class HelloWorld10
{
public static void main(String[] args)
{
/*
int i = 298;
byte b = (byte)(i); // 强转
System.out.println(“b = ” + b);
*/
float f = 10.88f;
int i = (int) (f);
System.out.println("i = " + i);
/*
byte b = 10;
int i = 20;
// b = b + i; // byte + int
// 强制类型转换
b = (byte)(b + i);
// i = b + i; // 正确
System.out.println("b = " + b);
*/
}
}
复合赋值运算符的自动强转功能 :
/*
* 搜狗输入法可以使用 shift 进行中英转换.
*
* 大写 : 按住 shift, 再按需要的字母.
*/
public class Demo1 {
public static void main(String[] args) {
// 32767
short s = 32767;
int i = 100;
s = (short) (s + i); // short + int -> 结果 int 类型
// 复合赋值运算符会 `自动完成` 了强转的功能
// 强转一定要小心, 因为数据一旦超过 `该类型` 的范围, 就会出错!
// s += i;
System.out.println(s);
}
}
5.表达式类型的自动提升 :
/*
表达式类型自动提升 :
byte + byte -> 结果为 int 类型.
short + short -> 结果为 int 类型.
整型中最常用的类型为 : int 类型.
为什么 byte , short 不常用呢 ??? 原因是因为 byte 和 short 表达式结果类型会自动提升.
*/
class HelloWorld11
{
public static void main(String[] args)
{
/*
byte b3 = 10;
b3 + 1; // int 类型
*/
short b1 = 110;
short b2 = 20;
short result = b1 + b2; // byte + byte
System.out.println("result = " + result);
}
}
6.Eclipse 快捷键
/*
alt + / 代码补全 ,代码提示
shift + enter 调到下一行开始处.
ctrl + / 添加或取消单行注释
ctrl + shift + / 添加多行注释
ctrl + shift + \ 取消多行注释
ctrl + D 删除光标所在的行
ctrl + 1 错误提示 ,给出解决方案
alt + 向下或向上键 移动当前选中的行
ctrl + alt + 向下或向上键 复制当前选中的行
ctrl + shift + F 格式化代码
*/
7.除法 / 取余运算
// arithmetic 算术
/*
* 算术运算符 : + - * / % (取余)
*
* 取余的使用场景 :
* 1. 判断奇偶数. 数值 % 2 0 (偶数) 1 (奇数)
* 2. 控制范围. 数值 % 6 结果 [0~5]
*/
public class ArithmeticDemo1 {
public static void main(String[] args) {
// 取余的使用场景2 : 控制结果的取值范围
int number2 = 6;
int result4 = number2 % 6;
System.out.println("result4 = " + result4);
// 取余的使用场景1 : 判断奇偶数. 数值 % 2
int number = 99;
int result3 = number % 2; // 0 (偶数) 1 (奇数)
System.out.println("result3 = " + result3);
// 演示2 : 取余的演示 :
// 取余 : (剩余)
// 商 : 3
// 余 : 1
int num1 = 10;
int num2 = 5;
int result2 = num1 % num2;
System.out.println("result2 = " + result2); // 0
// 演示1 : 除法的使用注意点 :
int i1 = 10;
int i2 = 4;
// int result = i1 / i2; // 2
// Type mismatch: cannot convert from float to int
// 类型不匹配 : 不能将 float 类型转换为 int 类型接收
float result = (float) i1 / i2; // float / int
System.out.println("result = " + result);
}
}
注意点 :
public class ArithmeticDemo4 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 4;
float result = num1 / num2; (float) num1 / num2; // 2.5
System.out.println(result); // 2.0
}
}
8.算术表达式的结果类型
/*
* 算术表达式的结果是什么类型的 : 大类型
*
* 注意1 : 浮点型 > 整型 double > float > long > int > short > byte
*/
public class ArithmeticDemo2 {
public static void main(String[] args) {
// 演示2 : long + float 结果为 float 类型
long num3 = 80L;
float num4 = 10.88f;
float result2 = num3 + num4;
System.out.println("result2 = " + result2);
// 演示1 : int + float 结果为 float 类型
int num1 = 10;
float num2 = 22.15f;
float result = num1 + num2;
System.out.println(result);
}
}
9.赋值 / 复合赋值运算符
/*
* 赋值运算符 : =
*
* 注意 : 一个变量只有在为其重新赋值的时候才会发生改变.
*
* 复合赋值运算符
: += -= *= /= %=
*
*/
public class ArithmeticDemo3 {
public static void main(String[] args) {
// 演示2 : 思考题
int number = 10;
number += 5;
System.out.println("number = " + number); // 15
number -= 5;
System.out.println("number = " + number); // 10
number *= 5;
System.out.println("number = " + number); // 50
number /= 5;
System.out.println("number = " + number); // 10
number %= 5;
System.out.println("number = " + number); // 0
// 演示1 :
/*
int number = 10;
// int result = number + 5; // number = 10 result = 15
// 如果在变量的 `自身` 基础上进行修改, 此时, 我们可以使用 `复合赋值运算符`
// number = number + 5; // number = 15
number += 5;
System.out.println("number = " + number);
*/
}
}
10.自增和自减运算
/*
* 自增和自减 : ++ –
*
* int number = 10;
*
* 前自增 后自增
* 变量本身 : ++number; 11 number++; 11
* 返回结果 : 11 10
*
*/
public class ArithmeticDemo5 {
public static void main(String[] args) {
// 思考三 :
int i = 8;
int j = 7;
int k = 6;
int result = i++ + --j + k--;
// 结果/本身 8/9 6/6 6/5
/*
int i1 = i++;
int i2 = --j;
int i3 = k--;
int result = i1 + i2 + i3;
*/
System.out.println(result); // 20
/* 思考二 :
int i = 8;
int result = i++ + ++i;
// 结果/本身 8/9 10/10
int i1 = i++; 8 变量本身 9
int i2 = ++i; 10 变量本身 10
int result = i1 + i2;
System.out.println(result); // 18
*/
/* 思考一 :
int i = 8;
int j = 5;
// 运算时, 获取的是结果的值.
int result = i++ + ++j;
// 结果/变量 8/9 6/6
int i1 = i++; 8
int j1 = ++j; 6
int result = i1 + j1; 14
System.out.println(result);
*/
// 演示1 :
/*
int number = 10;
// number++; // 后自增
// ++number; // 前自增
// int result = number++;
int result = ++number;
System.out.println("number = " + number + ", result = " + result);
*/
}
}
思考 : 浮点型和字符型也可以实现自增和自减吗?
public class ArithmeticDemo6 {
public static void main(String[] args) {
// 问题2 : 字符型可以自增和自减吗?
// 可以.
char ch = 'a';
ch++;
System.out.println("ch = " + ch);
// 问题1 : 浮点型可以自增和自减吗?
// 可以, 但是针对整型部分自增. 小数部分不变.
float f = 10.88f;
// f++;
++f;
// float result1 = f++; 10.88f;
// float result2 = ++f; 11.88f;
System.out.println("f = " + f);
}
}
11.编码表 : ASCII 美国标准信息交换代码
/*
* 编码表 : ASCII 美国标准信息交换代码
*
* 48 -> ‘0’
* 65 -> ‘A’
* 97 -> ‘a’
*/
public class ArithmeticDemo7 {
public static void main(String[] args) {
// char 和 int 可以互相赋值 : 字符在计算机底层是以 `数字` 来进行存储的, 只不过取出时, 需要使用 `编码表` 进行翻译.
// 输出显示的内容取决与 `变量的类型`.
char ch1 = '0';
char ch2 = 'A';
char ch3 = 'a';
System.out.println("ch1 = " + ch1 + ", ch2 = " + ch2 + ", ch3 = " + ch3);
char ch4 = 48;
char ch5 = 65;
char ch6 = 97;
System.out.println("ch4 = " + ch4 + ", ch5 = " + ch5 + ", ch6 = " + ch6);
int i1 = '0';
int i2 = 'A';
int i3 = 'a';
System.out.println("i1 = " + i1 + ", i2 = " + i2 + ", i3 = " + i3);
// char 二个字节 65535 short 二个字节 32676
char ch = '中';
System.out.println(ch);
System.out.println((int) ch);
/*
// 字符串
String str = "abc";
str++; 错误!
*/
}
}
12.逗号表达式
/*
* 逗号表达式 :
*
* 格式 :
* 数据类型 变量1 = 值1, 变量2 = 值2, 变量3 = 值3, … ;
*/
public class ArithmeticDemo8 {
public static void main(String[] args) {
// 单独定义变量
// char ch1 = ‘0’;
// char ch2 = ‘A’;
// char ch3 = ‘a’;
// 逗号表达式 : 定义相同类型的若干变量
char ch1 = '0', ch2 = 'A', ch3 = 'a';
System.out.println("ch1 = " + ch1 + ", ch2 = " + ch2 + ", ch3 = " + ch3);
// int ch4 = 48;
// int ch5 = 65;
// int ch6 = 97;
int ch4 = 48, ch5 = 65, ch6 = 97;
System.out.println("ch4 = " + ch4 + ", ch5 = " + ch5 + ", ch6 = " + ch6);
/* 不可以使用逗号表达式 :
int i = 10;
float f = 10.88f;
*/
}
}
13.比较运算符
/*
* 比较运算符 : 等于 == , 不等于 != , 大于 > , 小于 <, 大于等于 >= , 小于等于 <= .
*
* 作用 : 比较两个数值的关系. (等于, 不等, 大于, 小于 …)
* 结果 : true / false boolean 布尔类型
*/
public class ArithmeticDemo9 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 10;
boolean result = num1 != num2;
System.out.println("result = " + result);
}
}
14.三元运算符
/*
* 三元运算符 : boolean类型的结果 ? 值1 : 值2;
*
* true 返回值1
* false 返回值2
*
* 三元运算符的结果接收类型取决于 值1,值2
的类型. (值1 和 值2 必须是同一个类型)
*
* 单元运算符 : ++ –
* 双元运算符 : + - * / …
* 三元运算符 : ? :
*/
public class ArithmeticDemo10 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 10;
// boolean类型的结果 ? 值1 : 值2;
String result = num1 == num2 ? "真的" : "假的";
System.out.println(result);
boolean result2 = num1 == num2 ? true : false;
System.out.println(result2);
char result3 = num1 == num2 ? '是' : '否';
System.out.println(result3);
}
}
思考题 : 三个整型数值中求最大值.
public class ArithmeticDemo11 {
public static void main(String[] args) {
int num1 = 130;
int num2 = 150;
int num3 = 120;
// 需求 : 使用三元运算符获取最大值.
int secondMax = num1 > num2 ? num1 : num2;
int max = secondMax > num3 ? secondMax : num3;
System.out.println("max = " + max);
}
}
15.键盘录入 Scanner
1.Scanner 的基本使用步骤 :
package cn.itcast.scanner;
import java.util.Scanner;
/*
* Scanner 键盘录入 / 扫描器
*
* 1. 导包 import java.util.Scanner; 位置: 包下, 类上.
* 2. 创建一个键盘录入对象 Scanner sc = new Scanner(System.in); 位置: main方法中
* 3. 关闭资源 sc.close();
*
* 4. 操作数据 (提示, 并接收用户的输入) 获取用户输入的整型数据 : nextInt(); 位置: 创建和关闭资源的中间.
* int number = sc.nextInt(); String str = sc.nextLine();
*
* 5. 后期对用户输入的数据进行操作, 建议写在关闭资源之后.
*
*/
public class ScannerDemo1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个整型数据 : ");
int number = sc.nextInt(); // sc.nextInt() 效果就是造成了程序的等待.
sc.close();
// 后期操作 (查看)
System.out.println("number = " + number);
}
}
2.练习一 : 求和
思路讲解 :
// 1.1 导包
public class ScannerDemo2 {
public static void main(String[] args) {
// 需求 : 键盘录入两个数据并求和
// 1.2 创建一个键盘录入对象
// 1.4 操作数据 (提示, 并接收用户的输入)
// 1.3 关闭资源
// 2. 后期处理 (两个数据并求和)
// 2.1 将用户输入的两个整型数据进行相加
// 2.2 输出查看结果
}
}
代码实现 :
// 1.1 导包
import java.util.Scanner;
public class ScannerDemo2 {
public static void main(String[] args) {
// 需求 : 键盘录入两个数据并求和
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第一个整型数据 :");
int num1 = sc.nextInt();
System.out.println("亲, 请输入第二个整型数据 :");
int num2 = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (两个数据并求和)
// 2.1 将用户输入的两个整型数据进行相加
int sum = num1 + num2;
// 2.2 输出查看结果
System.out.println("sum = " + sum);
}
}
3.练习二 : 求最大值
// 1.1 导包
import java.util.Scanner;
public class ScannerDemo3 {
public static void main(String[] args) {
// 需求 : 键盘录入三个数据获取最大值
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的 3 次输入)
System.out.println("亲, 请输入第一个整型数据 :");
int num1 = sc.nextInt();
System.out.println("亲, 请输入第二个整型数据 :");
int num2 = sc.nextInt();
System.out.println("亲, 请输入第三个整型数据 :");
int num3 = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (三个数据获取最大值)
// 2.1 使用三元运算求较大值
// 格式 : boolean类型的结果 ? 值1 : 值2;
int secondMax = num1 > num2 ? num1 : num2;
// 2.2 使用三元运算求最大值
int max = secondMax > num3 ? secondMax : num3;
// 3. 输出, 并查看结果
System.out.println("max = " + max);
}
}
4.练习三 : 求5位数中的每一个数值
// 1.1 导包
import java.util.Scanner;
public class ScannerDemo4 {
public static void main(String[] args) {
// 需求 : 键盘录入一个整型万位数据, 获取 `个, 十, 百, 千, 万` 位上的每一个数值. 并查看
/*
* 87654
* 个位 : 4
* 十位 : 5
* 百位 : 6
* 千位 : 7
* 万位 : 8
*/
// 1.2 创建键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个5位数的万位数据 :");
int number = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (取每一个位数上的值)
int ge = number % 10;
int shi = number / 10 % 10;
int bai = number / 100 % 10;
int qian = number / 1000 % 10;
int wan = number / 10000;
System.out.println("ge : " + ge + ", shi : " + shi + ", bai : " + bai + ", qian : " + qian + ", wan : " + wan);
}
}
16.逻辑运算符
1.逻辑与和短路与 : 一假即假
public class LogicDemo1 {
public static void main(String[] args) {
// 案例 : 判断一个数是否是 大于0, 并且是 小于100
int number = -98;
// 比较运算符
// boolean result1 = number > 0;
// boolean result2 = number < 100;
// 逻辑运算符用于连接多个 `boolean 类型` 的结果, 逻辑运算符返回的结果还是 boolean 类型的结果.
// & 并且 (逻辑与) 口诀 : 一假即假
boolean result = number > 0 & number < 100;
System.out.println(result);
}
}
public class LogicDemo2 {
public static void main(String[] args) {
// && 短路与 & 逻辑与 口诀 : 一假即假
int i = 10;
// 短路与 : 只要结果能确认, 之后的所有语句都不会被执行. (开发中常量)
// boolean result = i++ > 10 && ++i > 5; result = false i = 11
// 逻辑与 : 无论结果是否能确认, 都会执行完毕所有的语句. (不常用)
boolean result = i++ > 10 & ++i > 5;
// 结果/变量 10/11 false 12/12 < 5 true
/*
* 结论 : 如果 i 变量的结果为 11, 说明了该语句 ++i > 5 没有被执行.
*/
System.out.println("result = " + result);
System.out.println("i = " + i);
}
}
课堂案例 :
public class LogicDemo4 {
public static void main(String[] args) {
// 案例 1 : 判断小明的 `语文, 数学, 英语` 成绩是否都及格.
int chineseScore = 100;
int mathScore = 90;
int englishScore = 59 + 1; // 应该累死 / 应改历史
// 判断
boolean result = chineseScore >= 60 && mathScore >= 60 && englishScore >= 60;
System.out.println(result);
}
}
2.逻辑或和短路或 : 一真即真
public class LogicDemo3 {
public static void main(String[] args) {
// | 逻辑或 || 短路或 (或者) 口诀 : 一真即真
int i = 10;
// | 逻辑或 无论是否能够确定结果, 所有的语句都会被执行. (不常用)
// boolean result = ++i > 10 | i++ > 20;
// 结果/变量 11/11 true 11/12 false
// || 短路或 一旦能够确认结果, 之后的语句就不会被执行. (开发中常用)
boolean result = ++i > 10 || i++ > 20;
// 结果/变量 11/11 true 11/12 false
System.out.println(result);
System.out.println(i);
}
}
课堂案例 :
public class LogicDemo5 {
public static void main(String[] args) {
// 案例2 : 判断一个人是否为 `男` 或者年龄大于等于 18 岁.
char gender = 'F'; // F / M Female 雌性 / Male 雄性 Woman / Man
int age = 18;
// 判断
boolean result = gender == 'M' || age >= 18;
System.out.println(result);
}
}
3.逻辑非 : 真变假, 假变真
public class LogicDemo6 {
public static void main(String[] args) {
// ! 逻辑非 口诀 : 真变假, 假变真. if 条件判断
/*
int num1 = 10;
int num2 = 20;
boolean result = ! (num1 > num2);
*/
boolean result1 = !true; // ! (num1 > num2);
boolean result2 = !false;
System.out.println("result1 = " + result1 + ", result2 = " + result2);
}
}
17.分支结构
1.if的第一种格式 :
/*
* 条件语句的第一种格式 : 分支结构
*
* if (boolean类型的条件) {
* 如果条件成立, 就会被执行的语句; 执行体 / 执行语句
* }
*
*/
// 1.1 导包
import java.util.Scanner;
public class ConditionDemo1 {
public static void main(String[] args) {
// 课堂练习 : 定义变量接收出生年份, 判断这个哥们是否成年, 如果成年了就显示一些信息给他看, 否则就算了.
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入您的出生年份 : ");
int year = sc.nextInt(); // 1996
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (判断这个哥们是否成年) if 语句
// 1996
if ((2017 - year) >= 18) {
// 3. 如果条件成立, 就显示一些信息给他看.
System.out.println("恭喜你, 你有资格欣赏以下漂亮的内容了. 亲, 一定要注意身体!");
}
// 最后 OVER ~
System.out.println("OVER ~");
}
}
2.if的第二种格式 :
/*
* if 条件语句的第二种格式 : 特点 : 要么执行 if 语句, 要么执行 else. bug 虫子 / 漏洞
*
* if (boolean类型的结果) {
* // true 执行语句
* } else {
* // false 执行语句
* }
*/
public class ConditionDemo2 {
public static void main(String[] args) {
// 需求 : 假设你有一个儿子, 写一个程序判断你儿子的英语成绩是否及格. 如果及格, 奖励, 否则, 打死.
System.out.println("亲, 欢迎使用成绩自动奖励系统.");
// 1. 定义一个变量用于存储成绩
int englishScore = 59;
// 2. 判断你儿子的英语成绩是否及格
if (englishScore >= 60) {
// 2.1 如果及格, 奖励
System.out.println("奖励了你儿子一辆 BMW.");
} else {
// 2.2 否则, 打死.
System.out.println("你儿子已经被打死了.");
}
System.out.println("亲, 感谢您的使用, 下次再来.");
}
}
3.if的第三种格式 :
/*
* if 条件语句的第三种格式: 特点: 只会执行其中的一句, 执行完毕就立刻跳出该 if 结构语句.
*
* if (boolean类型的结果) {
* 执行语句 1
* } else if (boolean类型的结果) {
* 执行语句 2
* } else if (boolean类型的结果) {
* 执行语句 3
* } else {
* 如果之前的都不成立, 最后执行该语句
* }
*
*/
// 1.1 导包
import java.util.Scanner;
public class ConditionDemo3 {
public static void main(String[] args) {
// 需求 : 键盘录入对一个学生的考试成绩进行等级的划分,
// 如果分数大于等于80分等级为优,
// 否则, 如果分数大于等于70等级为良,
// 否则, 如果分数大于等于60分等级为中,
// 否则, 等级为差.
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入您的考试成绩 :");
int score = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (成绩等级的划分) // please have a seat. 请坐.
if (score >= 80) {
// 如果分数大于等于80分等级为优,
System.out.println(score + " : 优");
} else if (score >= 70) {
// 否则, 如果分数大于等于70等级为良,
System.out.println(score + " : 良");
} else if (score >= 60) {
// 否则, 如果分数大于等于60分等级为中,
System.out.println(score + " : 中");
} else if (score >= 0) {
// 否则, 等级为差.
System.out.println(score + " : 差");
} else {
// 提示
System.out.println("亲, 请输入的成绩不合法! 没有等级.");
}
System.out.println("OVER ~ ");
}
}
补充版本 :
/*
* if 条件语句的第三种格式: 特点: 只会执行其中的一句, 执行完毕就立刻跳出该 if 结构语句.
*
* if (boolean类型的结果) {
* 执行语句 1
* } else if (boolean类型的结果) {
* 执行语句 2
* } else if (boolean类型的结果) {
* 执行语句 3
* } else {
* 如果之前的都不成立, 最后执行该语句
* }
*
*/
// 1.1 导包
import java.util.Scanner;
public class ConditionDemo4 {
public static void main(String[] args) {
// 需求 : 键盘录入对一个学生的考试成绩进行等级的划分,
// 如果分数大于等于80分等级为优,
// 否则, 如果分数大于等于70等级为良,
// 否则, 如果分数大于等于60分等级为中,
// 否则, 等级为差.
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入您的考试成绩 :");
int score = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 需求 : 成绩必须在 0 ~ 100 之间
// 判断
if (score >= 0 && score <= 100) {
// 条件成立, 说明成绩在 0 ~ 100 之间
// 2. 后期处理 (成绩等级的划分) // please have a seat. 请坐.
if (score >= 80) {
// 如果分数大于等于80分等级为优,
System.out.println(score + " : 优");
} else if (score >= 70) {
// 否则, 如果分数大于等于70等级为良,
System.out.println(score + " : 良");
} else if (score >= 60) {
// 否则, 如果分数大于等于60分等级为中,
System.out.println(score + " : 中");
} else if (score >= 0) {
// 否则, 等级为差.
System.out.println(score + " : 差");
}
} else {
// 成绩不在 0 ~ 100 之间
System.out.println("成绩不合法");
}
System.out.println("OVER ~ ");
}
}
4.案例 (根据月份输出对应的季节)
// 1.1 导包
import java.util.Scanner;
public class ConditionDemo5 {
public static void main(String[] args) {
/*
需求 : 根据用户给定的月份数据,显示月份在哪个季节.
3,4,5 春季
6,7,8 夏季
9,10,11 秋季
12,1,2 冬季
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入需要查询的月份 :");
int month = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (根据月份显示对应的季节)
// 2.1 使用 if ... else if ... else if ... else
// month
if (month >= 3 && month <= 5) {
// 3,4,5 春季
System.out.println(month + " : 对应的季节为春季.");
} else if (month >= 6 && month <= 8) {
// 6,7,8 夏季
System.out.println(month + " : 对应的季节为夏季.");
} else if (month >= 9 && month <= 11) {
// 9,10,11 秋季
System.out.println(month + " : 对应的季节为秋季.");
} else if (month == 12 || month == 1 || month == 2) {
// 12,1,2 冬季
System.out.println(month + " : 对应的季节为冬季.");
} else {
// 如果都不成立, 执行默认语法
System.out.println(month + " : 没有对应的季节.");
}
System.out.println("OVER ~");
}
}
5.switch语句
import java.util.Scanner;
/*
* switch 多条件判断语句 :
*
* switch (变量) { 开关
* case 值1 : 当…情况
* 执行语句 1;
* break; 跳出
* case 值2 :
* 执行语句 2;
* break;
* case 值3 :
* 执行语句 3;
* break;
* case 值4 :
* 执行语句 4;
* break;
* default :
* 默认执行语句;
* break;
* }
*/
public class ConditionDemo6 {
public static void main(String[] args) {
/*
需求 : 根据用户给定的月份数据,显示月份在哪个季节.
3,4,5 春季
6,7,8 夏季
9,10,11 秋季
12,1,2 冬季
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入需要查询的月份 :");
int month = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (根据月份显示对应的季节)
switch (month) {
case 3:
case 4:
case 5:
System.out.println(month + " : 对应的季节为春季.");
break;
case 6:
case 7:
case 8:
System.out.println(month + " : 对应的季节为夏季.");
break;
case 9:
case 10:
case 11:
System.out.println(month + " : 对应的季节为秋季.");
break;
case 12:
case 1:
case 2:
System.out.println(month + " : 对应的季节为冬季.");
break;
default :
// 默认执行语句...
System.out.println(month + " : 没有对应的季节.");
break;
}
/*switch (month) {
case 3:
System.out.println(month + " : 对应的季节为春季.");
break;
case 4:
System.out.println(month + " : 对应的季节为春季.");
break;
case 5:
System.out.println(month + " : 对应的季节为春季.");
break;
case 6:
System.out.println(month + " : 对应的季节为夏季.");
break;
case 7:
System.out.println(month + " : 对应的季节为夏季.");
break;
case 8:
System.out.println(month + " : 对应的季节为夏季.");
break;
case 9:
System.out.println(month + " : 对应的季节为秋季.");
break;
case 10:
System.out.println(month + " : 对应的季节为秋季.");
break;
case 11:
System.out.println(month + " : 对应的季节为秋季.");
break;
case 12:
System.out.println(month + " : 对应的季节为冬季.");
break;
case 1:
System.out.println(month + " : 对应的季节为冬季.");
break;
case 2:
System.out.println(month + " : 对应的季节为冬季.");
break;
default :
// 默认执行语句...
System.out.println(month + " : 没有对应的季节.");
break;
}*/
System.out.println("OVER ~");
}
}
18.循环结构
1.for 循环结构
/*
* for 循环结构 :
*
* for (; ; ) {
*
* }
*
* for (初始化变量; 循环条件; 增量) {
* 执行语句 / 循环体;
* }
*/
public class LoopDemo1 {
public static void main(String[] args) {
/*
* 问题 :
* 1. 代码重复.
* 2. 代码维护性差.
*/
// 需求 : 在控制台输出 1000 次 `我爱广州小蛮腰.`. 1 + 2 + 3 + 4... + 100 -> 5050
// 北京 `大裤衩`
for (int i = 0; i < 10; i++) {
// 执行语句 / 循环体;
System.out.println("我爱广州小蛮腰.");
}
/*
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
System.out.println("我爱北京天安门.");
*/
}
}
2.for循环求和
public class LoopDemo2 {
public static void main(String[] args) {
// 练习: 使用for循环, 求 1~100 之间所有整数的累加和.
// 方式一 :
// 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 ... + 100;
// 概念 : 依次访问固定范围内所有的整型数值, 这种操作行为被称为 `遍历`.
// 0. 定义一个 `框` 变量, 用于累加数值
int sum = 0;
// 1. 遍历 1~100 之间的所有数值
for (int i = 1; i <= 100; i++) {
// System.out.println(i);
// 2. 求和
sum += i; // sum = sum + i;
}
// 3. 查看框变量中的数值
System.out.println("sum = " + sum);
}
}
3.for循环求水仙花数
public class LoopDemo3 {
public static void main(String[] args) {
// 水仙花 : 指一个百位整型数值, 其个位数值, 十位数值, 百位数值的立方和等于该数值本身, 那么这个百位整型数值就是一个水仙花数.
// 100 ~ 999
// 153 3 * 3 * 3 + 5 * 5 * 5 + 1 * 1 * 1 == 153
// 1. 遍历 100 ~ 999 之间的所有整型数值
for (int i = 100; i <= 999; i++) {
// 2. 获取当前遍历的数值, 取出 `个, 十, 百` 上的每个数 / %
// 153 -> i
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 100;
// 3. 判断 if 语句
if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
// 4. 如果条件成立, 说明当前遍历的数值就是水仙花数
System.out.println(i);
}
}
}
}
4.while 循环结构
1.练习一 : 打印 10 次 我爱上海传智播客
/*
* 使用 while 循环输出 10 次 我爱上海传智播客
*
* for (; 循环条件; ) {
* 执行语句 / 循环体;
* }
*
* 格式 :
* while (循环条件) {
* 执行执行 / 循环体;
* }
*/
public class WhileLoopDemo1 {
public static void main(String[] args) {
/*
for (int i = 0; i < 10; i++) {
}
*/
/*
* for 和 while 循环的优势 :
*
* for 循环其实就是为 while 循环的最关键三处代码设定了其 `特殊的位置`.
*
* 使用建议 : 如果是一个 `确定次数` 的循环, 请选择使用 for 循环.
*/
// Syntax error 语法错误!
// 条件 : 必须是 boolean 类型的条件 false / true;
// 1. 定义初始化变量
int i = 0;
// 2. 循环条件
while (i < 10) {
// 4. 执行语句
System.out.println("我爱上海传智播客.");
// 3. 增量
i++;
}
}
}
2.练习二 : 累加 1 ~ 100 之间的整型和
public class WhileLoopDemo2 {
public static void main(String[] args) {
// 需求 : 使用 while 循环实现 1~100 之间的累加和
/*
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("sum = " + sum);
*/
// 1. 定义一个框变量
int sum = 0;
// 2.1 初始化变量
int i = 1;
// 2.2 循环条件
while (i <= 100) {
// 2.4 执行语句
sum += i;
// 2.3 增量
i++;
}
// 输出语句
System.out.println("sum = " + sum);
}
}
3.练习三 : 穷举
/*
* 什么是穷举 ? 一个一个的试, 效率最低, 也是最笨的方式.
* 案例 : 有一个人买了一筐鸡蛋 (至少有2个) 两个两个的数, 多一个, 三个三个的数, 多一个, 四个四个的数, 多一个. 请问 : 至少有几个鸡蛋 ?
* 思路 : 从2开始, 一个一个的试, 直到试出来为止.
*/
public class WhileLoopDemo3 {
public static void main(String[] args) {
// 1. 定义一个变量, 记录鸡蛋的个数
int egg = 2;
// 遍历 (死循环)
// while 循环使用的场景 : 未知 / 不确定循环次数的情况下, 请使用 while 循环.
while (true) {
// 判断, 鸡蛋至少有几个 ???
// 两个两个的数, 多一个, 三个三个的数, 多一个, 四个四个的数
if (egg % 2 == 1 && egg % 3 == 1 && egg % 4 == 1) {
// 如果条件满足, 说明当前鸡蛋的个数就是最少拥有的个数
System.out.println("egg = " + egg);
// 跳出整个 while 循环
break;
}
// 注意 : 鸡蛋判断的个数需要自增
egg++;
}
System.out.println("OVER ~");
/*
int num1 = 10;
int num2 = 20;
if (num1 > num2) {
// break cannot be used outside of a loop or a switch
// break 只能在循环和 switch 语句中使用.
break;
}
*/
}
}
/*
* 总结 : do-while 循环很少使用.
*
* do {
* 执行语句 / 循环体;
* } while (条件);
*
* 注意 : while 和 do-while 有什么区别 ???
*
* 1. 如果循环条件成立, 两者无区别.
* 2. 如果循环条件不成立, while 循环一次都不会执行, 而 do-while 至少会执行一次, 还有可能会造成死循环.
*/
public class DoWhileLoopDemo4 {
public static void main(String[] args) {
int i = 10;
while (i > 10) {
System.out.println("我来了...");
i++;
}
System.out.println("-----------------");
// 1. 初始化变量
int j = 10;
do {
// 4. 执行语句
System.out.println("我走了...");
// 3. 增量
j++;
// 2. 循环条件
} while (j > 10);
}
}
/*
* break 跳出当前循环 / 跳出 switch 结构
*/
public class BreakDemo5 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("I love you.");
// 需求 : i == 5, 结束了.
if (i == 4) {
break;
}
}
System.out.println("I hate you.");
System.out.println("I miss you.");
}
}
2.continue
/*
* continue : 结束 本次
循环, 继续下次循环.
*/
public class ContinueDemo6 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 需求 : I love you. 5 该跳语句不要输出.
if (i == 5) {
continue;
}
System.out.println("I love you. " + i);
}
System.out.println("I hate you.");
System.out.println("I miss you.");
}
}
public class ReviewDemo1 {
public static void main(String[] args) {
// 已知循环次数使用 while 循环, 在内部使用了 continue 关键字.
// for + continue 无问题 !
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue;
}
System.out.println(i);
}
// while + continue 有问题 !
int i = 0;
while (i < 10) {
if (i == 5) {
System.out.println(i);
continue;
}
System.out.println(i);
i++;
}
}
}
19.Random 随机数
1.Random 类的使用步骤 :
// 步骤一 : 导包
import java.util.Random;
/*
* Random 随机数
*
* 步骤一 : 导包 import java.util.Random; 位置: 包下, 类上
* 步骤二 : 创建随机数对象 Random r = new Random(); 位置: 类的主方法中
* 步骤三 : 直接使用 int number = r.nextInt(n); 说明 : 0~n-1 的范围
*/
public class RandomDemo1 {
public static void main(String[] args) {
// 步骤二 : 创建随机数对象
Random r = new Random();
for (int i = 0; i < 10; i++) {
// 步骤三 : 直接使用
int number = r.nextInt(11);
System.out.println(number);
}
}
}
2.练习 : 获取10个 [10~20] 之间的随机数
// 1.1 导包
import java.util.Random;
public class RandomDemo2 {
public static void main(String[] args) {
// 需求 : 随机生成十个 [10~20] 范围的随机数. 格式: 大值 - 小值 + 1
// 2~500 n => 500-2+1 => 499
// 1.2 创建随机数对象
Random r = new Random();
// 2. 使用 for 循环执行 10 次
for (int i = 0; i < 10; i++) {
// 1.3 直接使用
int number = r.nextInt(11) + 10; // 0+10 10+10
System.out.println(number);
}
}
}
3.小游戏 – 猜数字
需求 : 系统产生一个1-100之间的随机数,请猜出这个数据是多少。
// 1.1 导包 (随机数, 键盘录入)
import java.util.Random;
import java.util.Scanner;
public class RandomDemo3 {
public static void main(String[] args) {
// 案例 : 猜数字
// 随机生成一个 1~100 之间的随机数, 让用户不断猜, 需要给用户对应的提醒. (大了, 小了, 猜中了)
// 1.2 创建随机数对象
Random r = new Random();
// 1.3 直接操作 number 随机生成的数值
int number = r.nextInt(100) + 1;
System.out.println(number);
// 2.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 4. 添加一个循环
while (true) {
// 2.4 操作数据 (提示, 并接收用户的输入) guessNumber 猜测的数值
System.out.println("亲, 请输入你猜测的数值 :");
int guessNumber = sc.nextInt();
// 3. 判断 if ... else if ... else
if (guessNumber > number) {
// 大了
System.out.println("猜大了.");
} else if (guessNumber < number) {
// 小了
System.out.println("猜小了.");
} else {
// 猜中了 (不需要再次接收用户的输入了)
System.out.println("恭喜您, 您猜对了. ^v^");
// 猜中后, 需要跳出循环
break;
}
}
// 2.3 关闭资源
sc.close();
}
}
20.数组
1.数组的引入
思考问题 :
我们班有一个学习小组, 共5个人,保存每一个同学的成绩, 再将所有学生成绩的总和输出.
我们很容易想到的第一种方法 : 使用5个变量来存储每一位同学的成绩.
public class ArrayDemo1 {
public static void main(String[] args) {
// 需求 : 存储一个学习小组 50 位成员的成绩, 并统计该小组的总成绩.
// 1. 定义 5 个变量, 存储 5 位成员的成绩
int stu1 = 100;
int stu2 = 88;
int stu3 = 98;
int stu4 = 78;
int stu5 = 85;
// 2. 累加 5 为成员的成绩
int sum = stu1 + stu2 + stu3 + stu4 + stu5;
System.out.println("sum = " + sum);
}
}
2.数组的两种定义格式与数组的遍历
格式一 :
public class ArrayDemo2 {
public static void main(String[] args) {
// 需求 : 使用数组存储小组的成绩.
// 格式一 :
// 元素类型[] 数组名 = new 元素类型[长度];
int[] arr = new int[5];
// 存储
arr[0] = 100;
arr[1] = 98;
arr[2] = 88;
arr[3] = 78;
arr[4] = 85;
// 取出
System.out.println("arr[0] = " + arr[0]);
System.out.println("arr[1] = " + arr[1]);
System.out.println("arr[2] = " + arr[2]);
System.out.println("arr[3] = " + arr[3]);
System.out.println("arr[4] = " + arr[4]);
}
}
格式二 :
public class ArrayDemo3 {
public static void main(String[] args) {
// 需求 : 使用格式二存储, 并取出数据
// 格式二 :
// 元素类型[] 数组名 = new 元素类型[]{元素1, 元素2, 元素3, 元素4, 元素5};
int[] arr = new int[]{100, 98, 88, 78, 85}; // int num = 100;
// 依次访问数组中的每一个元素数组 : 遍历数组
// 系统提供了一个属性可以直接获取数组的长度 : 数组名.length;
int len = arr.length;
System.out.println("len = " + len);
// 定义一个框变量
int sum = 0;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
// 累加
sum += arr[i];
}
// 循环结束后, 查看框变量中的数值
System.out.println("sum = " + sum);
/*
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]);
*/
}
}
3.数组的回顾与两个常见的异常
/*
* 回顾 :
* 格式一 : 元素类型[] 数组名 = new 元素类型[长度]; 数组如果不进行初始化, 那么其元素也有默认初始值.
* 说明: 变量如果不初始化, 就不可以直接使用.
*
* 格式二 : 元素类型[] 数组名 = new 元素类型[]{元素1, 元素2, 元素3, 元素4, 元素5…};
* 注意点 : 如果一个数组提供了初始值, 那么该数组就不可以再定义长度. 因为程序可以直接根据初始值中的元素个数自动确定数组的长度.
*/
public class ArrayDemo1 {
public static void main(String[] args) {
// 1. 定义一个数组
// int[] arr = new int[5];
// Cannot define dimension expressions 维度表达式 / 数组的长度
// when an array initializer is provided 当一个数组已经提供了初始值
int[] arr = new int[]{10, 20, 30, 40, 50};
// 简写格式 : JVM编译时, 就根据左边的类型自动给右方的初始值添加数据类型. new int[]{10, 20, 30, 40, 50};
// int[] arr = {10, 20, 30, 40, 50};
// 遍历数组
// arr.length 中的 length 是系统给我们提供了. 所以我们在程序中可以直接使用, 表示获取该数组的长度.
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
/*
int num;
// The local variable 这个局部变量 num may not 可能没有 have been initialized 被完成初始化.
// 变量没有初始化之前是不可以使用的.
System.out.println(num);
*/
}
}
两个常见的数组异常 :
/*
* 问题1 : ArrayIndexOutOfBoundsException 数组下标越界异常.
*
* 问题2 : NullPointerException 空指针 / 空引用异常. 数组 / 对象 已经被清空了, 不能使用了.
* 说明 : Java 规定, 空对象 (null) 不可以在程序中再次被使用. (访问, 赋值 …)
*/
public class ArrayDemo2 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = {10, 20, 30, 40, 50};
// 2. 遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println("arr["+i+"] = " + arr[i]);
}
// 异常一 : ArrayIndexOutOfBoundsException 数组下标越界异常
System.out.println(arr[5]);
System.out.println(arr[0]);
// 将数组清空, 之后就表示 `整个数组` 就不可以在使用了.
arr = null;
// 异常二 : NullPointerException 空引用异常
System.out.println(arr[1]);
System.out.println("OVER~");
}
}
4.数组内存图解
5.数组的操作 – 最值
public class ArrayDemo4 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = new int[]{89, 76, 34, 56, 99, 12, 67, 100, 88};
// 需求1 : 获取该数组中的最大值 ???
// 1.0 定义一个 `擂台` 变量, 初始化数组的第 0 个元素.
int max = arr[0]; // 89
// 1.1 遍历数组, 遍历直接从第 1 个下标开始 (依次访问数组中的每一个元素)
for (int i = 1; i < arr.length; i++) {
// System.out.println(arr[i]);
// 1.2 比较
if (max < arr[i]) { // 99 < 67
// 条件成立, 说明 max 不是最大的. 需要重新赋值
max = arr[i]; // max = 99;
}
}
// 1.3 查看 `擂台` 变量
System.out.println("max = " + max);
// 需求2 : 获取该数组中的最小值 ???
// 2.1 定义一个 `擂台` 变量
int min = arr[0];
// 2.2 遍历数组
for (int i = 1; i < arr.length; i++) {
// 2.3 判断
if (min > arr[i]) {
min = arr[i];
}
}
// 2.4 查看擂台变量
System.out.println("min = " + min);
}
}
21.for 循环嵌套
1.思考题 : count
/*
* for 循环嵌套 : 外层每执行一次, 内层就会执行 所有次
.
*
* for (; ; ) {
* for (; ; ) {
* }
* }
*/
public class ForForDemo1 {
public static void main(String[] args) {
// 定义一个 `计数器` 变量
int count = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
// 在内层循环中, 累加计数器数值
count++;
}
}
System.out.println("count = " + count);
}
}
2.地球和太阳的故事
/*
* 太阳和地球的故事 :
*
* 公转 : 地球公转了一圈. 外层循环.
* 自转 : 地球完成了 365 次自转. 内层循环.
*
* for 循环嵌套 : 外层每循环一次, 内层就会完成 所有次
.
*
*/
public class ForForDemo2 {
public static void main(String[] args) {
// 公转
for (int i = 0; i < 2; i++) {
// 自转
for (int j = 0; j < 365; j++) {
System.out.println("地球完成了第"+(j+1)+"次自转.");
}
System.out.println("地球完成了第"+(i+1)+"次公转.");
}
}
}
3.案例 – 拼接两个数组中的数据
需求 : 数组如下 :
int[] arr1 = {18, 19, 20, 21};
String[] arr2 = {“貂蝉”, “王昭君”, “西施”, “杨玉环”, “刘德华”, “张学友”, “郭富城”, “黎明”};
要求 : 输出结果.
代码实现 :
public class ForForDemo3 {
public static void main(String[] args) {
// 1. 定义两个数组
int[] arr1 = {18, 19, 20, 21};
String[] arr2 = {"貂蝉", "王昭君", "西施", "杨玉环", "刘德华", "张学友", "郭富城", "黎明"};
// 2. 实现
for (int i = 0; i < arr2.length; i++) { // 外层 arr2 数组
// 4 < 4
for (int j = 0; j < arr1.length; j++) { // 内层 arr1 数组
// 输出 : 貂蝉18, 貂蝉19, 貂蝉20, 貂蝉21 字符串相加就是相拼接.
String str = arr2[i] + arr1[j];
// 如何换行 : 在内层循环的最后一次换行 arr1.length (4) - 1
if (j == arr1.length-1) {
System.out.println(str);
} else {
System.out.print(str + ", ");
}
}
}
/*
* 输出结果如下 :
*
* for 循环嵌套 : 外层每循环一次, 内层就会循环 `所有次`.
*
* 貂蝉18, 貂蝉19, 貂蝉20, 貂蝉21 行: `貂蝉` 是相同的.(外层循环) 18, 19, 20, 21 是变化的.(内层循环)
* 王昭君18, 王昭君19, 王昭君20, 王昭君21
* ......
*/
}
}
22.二维数组 (了解)
1.定义二维数组
练习 : 请将下列表格使用二维数组完成定义 :
组员1 组员2 组员3 组员4
第一小组 11 12
第二小组 21 22 23
第三小组 31 32 33 34
/*
* 二维数组 : 数组中的数组. 二维数组就是用来存储 一维数组
的. 二维数组元素不是存储数据, 而是一维数组.
*
* 格式一 : 元素类型[][] 数组名 = new int[二维数组长度][一维数组长度];
* 格式二 : 元素类型[][] 数组名 = new int[二维数组长度][];
* 格式三 : 元素类型[][] 数组名 = new int[][]{一维数组1, 一维数组2, 一维数组3 …};
* 格式四 : 元素类型[][] 数组名 = {一维数组1, 一维数组2, 一维数组3 …};
*/
public class ArrayDemo1 {
public static void main(String[] args) {
/*
组员1 组员2 组员3 组员4
第一小组 11 12
第二小组 21 22 23
第三小组 31 32 33 34
*/
// 分别定义三个 `一维数组`, 存储三组数组
// int[] arr1 = {11, 12};
// int[] arr2 = {21, 22, 23};
// int[] arr3 = {31, 32, 33, 34};
// 定义一个二维数组
// 元素类型[][] 数组名 = new int[][]{一维数组1, 一维数组2, 一维数组3 ...};
// int[][] arr = new int[][]{arr1, arr2, arr3};
// 二维数组中存储的是一维数组, 不是数值.
// int[][] arr = new int[3][]; 默认初始化 null
int[][] arr = new int[][]{{11, 12}, {21, 22, 23}, {31, 32, 33, 34}};
// 一个一个取
/*
* for 循环嵌套 : 外层每循环一次, 内层就会循环 `所有次`.
* 说明 :
* 如果外层是第 0 次循环, 内层就循环 `两次`. j < arr[0].length
* 如果外层是第 1 次循环, 内层就循环 `三次`. j < arr[1].length
* 如果外层是第 2 次循环, 内层就循环 `四次`. j < arr[2].length
*
* 外层循环的条件是 int i = 0; i < arr.length; i++ i < arr.length
* 内层循环的条件是根据一维数组的长度决定的.
*
*/
// 二维数组的遍历 :
for (int i = 0; i < arr.length; i++) { // 二维数组的长度
for (int j = 0; j < arr[i].length; j++) { // 二维数组中对应一维数组的长度
System.out.println(arr[i][j]);
}
}
/*
System.out.println(arr.length); // 3
System.out.println(arr[0].length); // 2
System.out.println(arr[1].length); // 3
System.out.println(arr[2].length); // 4
*/
/*
System.out.println(arr[0][0]);
System.out.println(arr[0][1]);
System.out.println(arr[1][0]);
System.out.println(arr[1][1]);
System.out.println(arr[1][2]);
System.out.println(arr[2][0]);
System.out.println(arr[2][1]);
System.out.println(arr[2][2]);
System.out.println(arr[2][3]);
*/
// System.out.println(arr[2][4]);
/*
System.out.println(arr[0]);
System.out.println(arr);
System.out.println(arr[2][0]); // 31
System.out.println(arr[1][2]); // 23
*/
}
}
2.二维数组练习 :
课堂练习 : 通过案例熟悉二维数组的使用, 例如要统计一个公司三个销售小组中每个小组的总销售额以及整个公司的销售额.
public class ArrayDemo2 {
public static void main(String[] args) {
/*
组员1 组员2 组员3 组员4
第一小组 11 12
第二小组 21 22 23
第三小组 31 32 33 34
*/
// 需求 : 统计每一个小组的销售额, 及总公司的销售额.
// 二维数组中存储的是一维数组, 不是数值.
int[][] arr = new int[][]{{11, 12}, {21, 22, 23}, {31, 32, 33, 34}};
// 定义两个 `框` 变量, 一个用于统计小组, 另一个统计总公司.
int companySum = 0;
int groupSum = 0;
// 二维数组的遍历 :
for (int i = 0; i < arr.length; i++) {
// 清空 groupSum 中累加的数值
groupSum = 0;
for (int j = 0; j < arr[i].length; j++) {
// System.out.println(arr[i][j]);
// 累加每一个小组的销售额
groupSum += arr[i][j];
}
// 输出小组
System.out.println("第"+(i+1)+"个小组的销售额为: " + groupSum);
// 累加总公司的销售额
companySum += groupSum;
}
// 输出
System.out.println("总公司的销售额为 : " + companySum);
}
}
23.方法
程序中的有些代码实现的是同一个 功能模块
. 可以将这个 功能模块
的代码全部抽取为一个 方法
.
Java 方法的作用 : 将相同代码实现的功能抽取为一个独立的功能模块, 如果需要执行这个功能, 无需重复编写代码, 而是直接调用方法即可.
1.案例一 : 两个整型数值求和
完整代码 :
/*
* 一个程序启动后, 只会执行 main 方法. 自定义的所有方法都不会被执行. 除非 手动代码调用
.
*/
public class MethodDemo3 {
public static void main(String[] args) {
// 案例1 : 两个整型数值求和方法的编写
// 调用自定义方法
// 格式 : 方法名(实参1, 实参2);
/*
* 如何一个方法有返回值, 不能单独调用. 单独调用毫无效果.
*/
// 单独调用
// sum(10, 20);
// 输出调用 (不推荐)
// System.out.println(sum(10, 20));
// 接收调用 (推荐使用)
int result = sum(10, 20);
System.out.println(result);
// 接收调用的好处就是可以获取结果做后期的处理...
/*if (result > 60) {
}*/
/*
int num1 = 10;
int num2 = 20;
// 需求 : 将求和的功能抽取为一个方法呢 ??? 以后每次求和, 不用编写重复的代码, 而是直接调用 ???
int sum = num1 + num2; // 虽然就一行, 想象有 100 行.
System.out.println(sum);
// 再求一次和
int num3 = 110;
int num4 = 120;
int sum2 = num3 + num4;
System.out.println(sum2);
*/
System.out.println("OVER~");
}
/*
* 功能模块只能抽取为 `方法`.
* 方法的定义格式 :
*
* 修饰符 返回值类型 方法名(参数类型1 参数名1, 参数类型2 参数名2 ...) {}
*
* 明确1 : 返回值类型
* 明确2 : 参数列表
*/
public static int sum(int a, int b) {
int s = a + b;
return s;
}
}
简洁代码 :
public class MethodDemo4 {
public static void main(String[] args) {
// 接收调用 (推荐使用)
int result = sum(10, 20);
System.out.println(result);
}
// 定义了一个求和的方法
public static int sum(int a, int b) {
int s = a + b;
return s;
}
}
2.案例二 : 键盘录入两个整型数据, 返回两个值中的较大值.
// 1.1 导包
import java.util.Scanner;
public class MethodDemo5 {
public static void main(String[] args) {
// 案例二 : 键盘录入两个整型数据, 返回两个值中的较大值.
// 1.2 创建一个键盘录入对象
// ctrl + 2 -> L
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第一个整型数据 :");
int num1 = sc.nextInt();
System.out.println("亲, 请输入第二个整型数据 :");
int num2 = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (返回两个值中的较大值)
// 调用方法, 获取较大值
int max = getMax(num1, num2);
System.out.println("max = " + max);
}
/*
* 定义一个方法, 获取两个整型的较大值
*
* 明确1 : 返回值类型 int
* 明确2 : 参数列表 int num1, int num2
*/
public static int getMax(int num1, int num2) {
/* if...else...
if (num1 > num2) {
return num1;
} else {
return num2;
}*/
// 三元运算符
// int max = num1 > num2 ? num1 : num2;
// return max;
// 三元运算符的简写
return num1 > num2 ? num1 : num2;
}
}
3.案例三 : 键盘录入比较两个数据是否相等
// 1.1 导包
import java.util.Scanner;
public class MethodDemo6 {
public static void main(String[] args) {
// 案例三 : 键盘录入比较两个数据是否相等
// 1.2 创建一个键盘录入对象
// ctrl + 2 -> L
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第一个整型数据 :");
int num1 = sc.nextInt();
System.out.println("亲, 请输入第二个整型数据 :");
int num2 = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (比较相等) compare
// 调用方法进行判断实现
boolean result = compare(num1, num2);
System.out.println("result = " + result);
}
/*
* 定义一个方法, 判断两个整型数值是否相等
*
* 明确1 : 返回值类型 boolean
* 明确2 : 参数列表 int n1, int n2
*/
public static boolean compare(int n1, int n2) {
/* 实现一 : if...else...
if (n1 == n2) {
return true;
} else {
return false;
} */
// 实现二 : 三元运算符
// boolean result = n1 == n2 ? true : false;
// return result;
// 实现三 : 简写
// return n1 == n2 ? true : false;
// 实现四 : 再简写
// `==` 比较运算符返回的就是 boolean 类型的结果.
return n1 == n2;
}
}
4.案例四 : 键盘录入获取三个数据中的最大值
// 1.1 导包
import java.util.Scanner;
public class ArrayDemo5 {
public static void main(String[] args) {
// 案例四 : 键盘录入获取三个数据中的最大值
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第一个整型数据 : ");
int num1 = sc.nextInt();
System.out.println("亲, 请输入第二个整型数据 : ");
int num2 = sc.nextInt();
System.out.println("亲, 请输入第三个整型数据 : ");
int num3 = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (获取三个数据中的最大值)
// 调用方法, 获取最大值
int max = getMax(num1, num2, num3);
System.out.println("max = " + max);
}
/*
* 定义一个方法 : 获取三个整型的最大值
*
* 修饰符 返回值类型 方法名(参数类型1 参数名1, 参数类型2 参数名2...) {}
*
* 明确1 : 返回值类型 int
* 明确2 : 参数列表 int num1, int num2, int num3
*/
public static int getMax(int num1, int num2, int num3) {
/* 实现一 : if ... else ...
if (num1 > num2) {
// num1 大
if (num1 > num3) {
// num1 最大
return num1;
} else {
// num3 最大
return num3;
}
} else {
// num2 大
if (num2 > num3) {
// num2 最大
return num2;
} else {
// num3 最大
return num3;
}
} */
// 实现二 : 三元运算符
int secondMax = num1 > num2 ? num1 : num2;
int max = secondMax > num3 ? secondMax : num3;
return max;
}
}
5.案例五 : void修饰的方法的调用
public class ArrayDemo6 {
public static void main(String[] args) {
// 案例五 : void修饰的方法的调用
// 打印 10 次 `Hello World`
// 调用一 : 直接调用
printHelloWorld();
// args -> arguments 参数
// The method println(boolean) in the type PrintStream is not applicable 不兼容 for the arguments 参数 (void)
// 在 PrintStream 类型中的 println 方法不兼容 void 的方法返回值.
// void 返回值的方法, 不可以打印调用
// 调用二 : 打印调用 (错误)
// System.out.println(printHelloWorld());
// void is an invalid type for the variable hello
// void 不可以作为变量的接收类型
// 调用三 : 接收调用 (错误)
// void hello = printHelloWorld();
}
/*
* 定义一个方法, 打印 10 次 `Hello World`
*
* 明确1 : 返回值类型 没有返回值类型, 需要写 void
* 明确2 : 参数列表 不需要参数列表, 小括号中可以什么都不写
*/
public static void printHelloWorld() {
for (int i = 0; i < 10; i++) {
System.out.println("hello world.");
}
}
}
6.案例六 : 打印从1到n之间的整型数据
public class ArrayDemo7 {
public static void main(String[] args) {
// 案例六 : 打印从1到n之间的整型数据
// 调用方法 : 5 1, 2, 3, 4, 5
printNumber(5);
System.out.println("-------------");
// 调用方法 : 10 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
printNumber(10);
}
/*
* 定义一个方法, 打印 1~n 之间的整型数值
*
* 明确1 : 返回值类型 不需要返回值类型, 写 void
* 明确2 : 参数列表 int n
*/
public static void printNumber(int n) {
// 直接遍历
for (int i = 1; i <= n; i++) {
System.out.println(i);
}
}
}
7.案例七 : 打印所有的水仙花数
public class ArrayDemo8 {
public static void main(String[] args) {
// 案例七 : 打印所有的水仙花数
printWaterFlower();
}
/*
* 定义一个方法, 打印水仙花数.
*
* 明确1 : 返回值类型 没有返回值类型, 写 void
* 明确2 : 参数列表 不需要, 小括号中可以什么都不写, 小括号是不可以省略的.
*/
public static void printWaterFlower() {
// 1. 遍历 100 ~ 999 之间的每一个整型数值
for (int i = 100; i <= 999; i++) {
// 2. 获取当前遍历数值的 `个, 十, 百` 位上的数字
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 100;
// 3. 判断
if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
// 4. 如果条件成立, 说明当前遍历的数值就是水仙花数, 打印即可
System.out.println(i);
}
}
}
}
24.方法重载 overload
1.重载介绍与实现
需求 : 使用方法实现对两个整数相加,对三个整数相加,以及对两个小数相加的功能.
/*
* 定义一个方法, 方法就是抽取 功能模块
. 既然功能都相同, 为什么我们不去定义相同的名字呢 ???
*
* 方法可以定义相同的名称. 但是一定要保证 参数类型 / 参数长度
必须不相同.
*
* 方法重载的注意点 :
* 1. 相同 : 方法名必须相同.
* 2. 不同 : 参数类型 或 长度 必须不同. int, float float, int
* 3. 无关 : 返回值类型 和 参数名无关.
*/
public class OverloadDemo1 {
public static void main(String[] args) {
// 需求 : 使用方法实现对两个整数相加,对三个整数相加,以及对两个小数相加的功能
float result = sum(10.88f, 12.34f);
System.out.println(result);
// 保留两位小数
System.out.printf("%.2f", result); // f -> format 格式化 %.2f float类型保留两位小数
System.out.println();
int sum = sum(10, 20);
System.out.println(sum);
}
// 两个整数相加
public static int sum(int num1, int num2) {
System.out.println("sum(int, int) ...");
return num1 + num2;
}
// Duplicate method sum(int, int) in type OverloadDemo1
// 在 OverloadDemo1 类中 sum(int, int) 该方法重复了.
// 记住 : 一个方法的确定是通过 `方法名 + 参数列表` 共同来确定的. 跟返回值类型与参数名称无关.
/*public static double sum(int a, int b) {
return a + b;
}*/
// 三个整数相加
public static int sum(int num1, int num2, int num3) {
System.out.println("sum(int, int, int) ...");
return num1 + num2 + num3;
}
// 两个小数相加
public static float sum(float num1, float num2) {
System.out.println("sum(float, float) ...");
return num1 + num2;
}
}
25.方法与数组练习
1.基本数据类型作为参数传递 (值传递)
2.数组名作为参数传递 (引用传递)
3.定义方法遍历数组, 并按指定格式输出
public class PracticeDemo1 {
public static void main(String[] args) {
// 案例 : 定义方法遍历数组, 并按指定格式输出
// 格式 : [10, 20, 30, 40, 50]
// 1. 定义一个数组
int[] arr = new int[] {110, 120, 130, 140, 150};
// 2. 直接调用
printArray(arr);
// 3. 调用数组转字符串的方法
String result = arrayToString(arr);
System.out.println(result);
}
/*
* 定义方法 : arrayToString to 转换
* 明确1 : 返回值类型 有, String 返回一个字符串
* 明确2 : 参数列表 有, 参数是数组.
*/
public static String arrayToString(int[] array) {
// 格式 : [10, 20, 30, 40, 50]
String temp = "[";
// 遍历数组
for (int i = 0; i < array.length; i++) {
// 判断
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
/*
* 定义方法 :
* 明确1 : 返回值类型 没有.
* 明确2 : 参数列表 有, 参数是数组.
*/
public static void printArray(int[] array) {
// 格式 : [10, 20, 30, 40, 50]
System.out.print("[");
// 遍历数组
for (int i = 0; i < array.length; i++) {
// 判断
if (i == array.length - 1) {
System.out.println(array[i] + "]");
} else {
System.out.print(array[i] + ", ");
}
}
}
}
26.键盘录入,数组与方法综合练习 : 裁判评分
/*
* 需求:在编程竞赛中,有6个评委为参赛的选手打分,分数为0-100的整数分。
* 选手的最后得分为:去掉一个最高分和一个最低分后 其余4个选手的平均值。
* 请写代码实现。(不考虑小数部分)
*
* 分析:
* 1:定义一个长度为6的数组。
* 2:通过键盘录入的方式给出评委的分数
* 3:写方法实现获取数组中的最大值,最小值
* 4:写方法实现数组元素的求和
* 5:平均分: (和-最高分-最低分)/(arr.length-2)
* 6:输出分数即可
*/
public class PracticeDemo1 {
public static void main(String[] args) {
// 案例 : 定义方法遍历数组, 并按指定格式输出
// 格式 : [10, 20, 30, 40, 50]
// 1. 定义一个数组
int[] arr = new int[] {110, 120, 130, 140, 150};
// 2. 直接调用
printArray(arr);
// 3. 调用数组转字符串的方法
String result = arrayToString(arr);
System.out.println(result);
}
/*
* 定义方法 : arrayToString to 转换
* 明确1 : 返回值类型 有, String 返回一个字符串
* 明确2 : 参数列表 有, 参数是数组.
*/
public static String arrayToString(int[] array) {
// 格式 : [10, 20, 30, 40, 50]
String temp = "[";
// 遍历数组
for (int i = 0; i < array.length; i++) {
// 判断
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
/*
* 定义方法 :
* 明确1 : 返回值类型 没有.
* 明确2 : 参数列表 有, 参数是数组.
*/
public static void printArray(int[] array) {
// 格式 : [10, 20, 30, 40, 50]
System.out.print("[");
// 遍历数组
for (int i = 0; i < array.length; i++) {
// 判断
if (i == array.length - 1) {
System.out.println(array[i] + "]");
} else {
System.out.print(array[i] + ", ");
}
}
}
}
27.基础语法练习
1.打印5位数中的所有回文数
说明 : 什么是回文数呢? 举例:12321是回文数,个位与万位相同,十位与千位相同。
public class PracticeDemo3 {
// 主方法是主要的业务逻辑 : 程序的最主要流程
public static void main(String[] args) {
/*
案例 : 打印5位数中的所有回文数
说明 : 什么是回文数呢? 举例:12321 是回文数,个位与万位相同,十位与千位相同。
*/
getNumber();
}
// 定义一个方法, 作用 : 打印 5 位数的回文数
// 确定1 : 返回值类型 不需要 void
// 确定2 : 参数列表 不需要 10000 ~ 99999
public static void getNumber() {
// 1. 遍历 10000 ~ 99999 之间的每一个整型数值
for (int i = 10000; i <= 99999; i++) {
// 举例:12321 是回文数,个位与万位相同,十位与千位相同
// 2. 获取当前遍历数值的 `个, 十, 千, 万` 位上的数值
int ge = i % 10;
int shi = i / 10 % 10;
int qian = i / 1000 % 10;
int wan = i / 10000;
// 3. 判断
if (ge == wan && shi == qian) {
// 输出查看, 即是回文数
System.out.println(i);
}
}
}
}
2.不死神兔问题
/*
* 需求:
* 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,
* 假如兔子都不死,问第二十个月的兔子对数为多少?
*
* 规律:
* 第一个月:1
* 第二个月:1
* 第三个月:2
* 第四个月:3
* 第五个月:5
* …
*
* 规律:从第三个月开始,每个月的兔子对数是前两个月的兔子对数之和。
* 第一个月和第二个月的兔子对数是1
* 分析:
* int[] arr = new int[20];
*
* arr[0] = 1;
* arr[1] = 1;
*
* arr[2] = arr[0] + arr[1];
* arr[3] = arr[1] + arr[2];
* arr[4] = arr[2] + arr[3];
* …
*/
public class PracticeDemo4 {
public static void main(String[] args) {
int count = getRabbitCount(20);
System.out.println("count = " + count);
}
// 定义一个方法 : 获取兔子的数量
public static int getRabbitCount(int month) {
/*
* 需求:
* 有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,
* 假如兔子都不死,问第二十个月的兔子对数为多少?
*
* 规律:
* 第一个月:1
* 第二个月:1
* 第三个月:2
* 第四个月:3
* 第五个月:5
* ...
* 1、1、2、3、5、8、13、21、34、55、89、144、233、377、610、987、1597、2584、4181、6765、10946、17711、28657、46368…
*
* 规律:从第三个月开始,每个月的兔子对数是前两个月的兔子对数之和。
* 第一个月和第二个月的兔子对数是1
*/
// 第 20 个月数量 20个月 = 19个月 + 18个月 ...... 第3个月 = 第二个月 + 第一个月
// 传入的参数 : month = 20
// 1. 定义一个数组, 数组长度为 20 (month 的长度)
int[] arr = new int[month];
// 2. 定义前两个月数量 (因为这两个月是没有规律的)
arr[0] = 1;
arr[1] = 1;
// 3. 从第三个月开始, 数量就有规律了.
// 第3个月 = 第二个月 + 第一个月
for (int i = 2; i < month; i++) {
// i [2~19] 从 2 下标开始 到 19 下标中所有元素都在循环中完成了赋值
arr[i] = arr[i-1] + arr[i-2];
}
/*
arr[2] = arr[1] + arr[0];
arr[3] = arr[2] + arr[1];
arr[4] = arr[3] + arr[2];
arr[5] = arr[4] + arr[3];
arr[6] = arr[5] + arr[4];
...
*/
// 4. 返回 month-1 下标中对应的元素数值
return arr[month - 1];
}
}
3.求数组中满足要求的元素和
(1)定义一个int类型的一维数组,内容为{171,72,19,16,118,51,210,7,18}
(2)求出该数组中满足要求的元素和。
要求:求和的元素的个位和十位不能包含7,并且只能为偶数。
public class PracticeDemo5 {
public static void main(String[] args) {
/*
(1) 定义一个int类型的一维数组,内容为 {171, 72, 19, 16, 118, 51, 210, 7, 18}
(2) 求出该数组中满足要求的元素和。
要求:求和的元素的个位和十位不能包含7,并且只能为偶数。
*/
// 1. 定义一个数组
int[] arr = {171, 72, 19, 16, 118, 51, 210, 7, 18};
// 2. 调用方法
int sum = getSumByCondition(arr);
System.out.println(sum);
}
/*
* 定义一个方法 : 获取指定要求的元素和
*
* 明确1 : 返回值类型 int
* 明确2 : 参数列表 int[] array
*/
public static int getSumByCondition(int[] array) {
// 要求:求和的元素的个位和十位不能包含7,并且只能为偶数。
// 4.1 定义一个 `框` 变量, 用于累加满足条件的元素和
int sum = 0;
// 1. 遍历数组
for (int i = 0; i < array.length; i++) {
// 2. 获取当前遍历的元素, 获取该元素的 `个位, 十位` 上的数值
int number = array[i];
int ge = number % 10;
int shi = number / 10 % 10;
// 3. 判断
if (ge != 7 && shi != 7 && number % 2 == 0) {
// 4.2 如果条件满足, 需要累加
// System.out.println(number);
sum += number;
}
}
// 5. 返回框变量
return sum;
}
}
4.数组反转
public class PracticeDemo6 {
public static void main(String[] args) {
// 案例 : 数组反转
int[] array = {12, 23, 34, 45, 56}; // {56, 45, 34, 23, 12};
String str = arrayToString(array);
System.out.println("反转前 : " + str);
// 方法 : {56, 45, 34, 23, 12};
// 调用方法, 反转数组
reverseArray(array);
// 查看反转后的数组
String result = arrayToString(array);
System.out.println("反转后 : " + result);
/*
// 交换数值 :
int num1 = 10;
int num2 = 20;
// 写代码
// num1 = num2; // num1 = 20
// num2 = num1; // num2 = 20
// 第一种 : 使用一个 `第三方` 变量实现两个数值的交换
int temp = num1;
num1 = num2;
num2 = temp;
System.out.println("num1 = " + num1 + ", num2 = " + num2);
*/
}
// 定义一个方法, 将数组转换字符串返回
public static String arrayToString(int[] array) {
String temp = "[";
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
// 定义一个方法, 反转数组
public static void reverseArray(int[] array) {
for (int start = 0, end = array.length-1; start < end; start++, end--) {
// 交换
int temp = array[start];
array[start] = array[end];
array[end] = temp;
}
}
}
5.数组基本查找
需求: 在一个整型数组中查找指定元素第一次出现的下标,如果没有这个元素就打印 -1.
逐一遍历,进行元素的对比.
public class PracticeDemo7 {
public static void main(String[] args) {
/*
需求: 在一个整型数组中查找指定元素第一次出现的下标,如果没有这个元素就打印 -1.
实现 : 逐一遍历,进行元素的对比.
*/
int[] arr = {56, 34, 98, 87, 66, 29};
// 定义方法 : 66 -> 4 88 -> -1
int index = searchIndex(arr, 66);
System.out.println(index);
}
/*
* 定义一个方法 : 获取元素在数组中对应的下标
*
* 明确1 : 返回值类型 int类型
* 明确2 : 参数列表 参数1 : 数组 参数2 : 数值
*/
public static int searchIndex(int[] array, int key) {
// 0. 定义一个需要返回的下标变量, 初始值为 -1
int index = -1;
// 1. 遍历数组
for (int i = 0; i < array.length; i++) {
// 2. 判断
// 66 == 66
if (array[i] == key) {
// 3. 如果条件成立, 说明当前遍历的 i 就是需要查找的下标, 记录下标
index = i;
// 跳出循环
break;
}
}
return index;
}
public static int searchIndex1(int[] array, int key) {
// 1. 遍历数组
for (int i = 0; i < array.length; i++) {
// 2. 判断
// 66 == 66
if (array[i] == key) {
// 3. 如果条件成立, 说明当前遍历的 i 就是需要查找的下标
return i;
}
}
return -1;
}
}
6.数据加密
/*
* 需求:键盘录入数据,要求数据是四位的整数,现需要对数据进行加密,加密规则如下:
* 每位数字都加上5,然后除以10的余数代替该数字,
* 再将第一位和第四位交换,第二位和第三位交换,
* 请把加密后的数据输出到控制台
*
* 举例:
* 4567
* 把这个四位数分成个,十,百,千存储到数组中
* {4,5,6,7};
* 每位数字都加上5:
* {9,10,11,12}
* 然后除以10的余数代替该数字:
* {9,0,1,2}
* 再将第一位和第四位交换,第二位和第三位交换:
* {9,0,1,2} {2,1,0,9}
* C:输出加密后的数据
*/
// 1.1 导包
import java.util.Scanner;
public class PracticeDemo8 {
public static void main(String[] args) {
// 案例需求:键盘录入数据,要求数据是四位的整数,现需要对数据进行加密
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个四位的整型数值 :");
int number = sc.nextInt();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (调用方法, 实现加密)
String result = encryption(number);
System.out.println(result);
}
// 定义一个方法, 实现数据的加密
public static String encryption(int number) {
/*
* 需求 : 加密规则如下:
* 每位数字都加上5,然后除以10的余数代替该数字,
* 再将第一位和第四位交换,第二位和第三位交换,
* 请把加密后的数据输出到控制台
*
* 举例:
* 4567
* 把这个四位数分成个,十,百,千存储到数组中
* {4,5,6,7};
* 每位数字都加上5:
* {9,10,11,12}
* 然后除以10的余数代替该数字:
* {9,0,1,2}
* 再将第一位和第四位交换,第二位和第三位交换:
* {9,0,1,2} {2,1,0,9}
* C:输出加密后的数据
*/
// number = 4567;
// 1. 将传入的参数中的 `个, 十, 百, 千` 位上的数值取出
int ge = number % 10;
int shi = number / 10 % 10;
int bai = number / 100 % 10;
int qian = number / 1000;
// 2. 将获取的每一个数值存储到数组对应的下标中 {4,5,6,7}
int[] arr = new int[]{qian, bai, shi, ge};
// 3. 遍历数组
for (int i = 0; i < arr.length; i++) {
// 每位数字都加上5, 然后除以10的余数代替该数字
// 实现方式一 :
// 3.1 取出
int num = arr[i];
// 3.2 操作
num += 5; // 每位数字都加上5
num %= 10; // 10的余数
// 3.3 重新赋值
arr[i] = num;
// 实现方式二 :
// 记住 : 直接对数组中的对应下标元素进行操作 (无需取出和赋值)
// arr[i] += 5; // 每位数字都加上5
// arr[i] %= 10; // 10的余数
}
// {9,0,1,2}
// 4. 交换
// 4.1 第一次 : 0 下标和 3 下标交换
int temp1 = arr[0];
arr[0] = arr[3];
arr[3] = temp1;
// 4.2 第二次 : 1 下标和 2 下标交换
int temp2 = arr[1];
arr[1] = arr[2];
arr[2] = temp2;
// 5. 返回字符串, 调用数组转字符串
String result = arrayToString(arr);
return result;
}
// 定义一个方法, 将数组转换字符串返回
public static String arrayToString(int[] array) {
String temp = "[";
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
}
28.面向对象
1.回顾与演示 (数组工具类)
面向过程实现 :
/*
* 需求 : 要完成数组的 最大值, 最小值, 求和, 打印 ...
的功能
*/
public class ArrayDemo1 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = {89, 34, 67, 88, 65, 23, 61};
int[] arr2 = {893, 324, 617, 838, 645, 233, 611};
// 2. 调用方法, 获取最大值
int max = getMax(arr2);
System.out.println(max);
// 3. 调用方法, 获取最小值
int min = getMin(arr);
System.out.println(min);
// 4. 调用方法, 获取累加和
int sum = getSum(arr);
System.out.println(sum);
// 5. 调用方法, 将数组转成字符串
String result = arrayToString(arr);
System.out.println(result);
}
// 调用方法, 将数组转成字符串
public static String arrayToString(int[] array) {
String temp = "[";
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
// 调用方法, 获取累加
public static int getSum(int[] array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
// 调用方法, 获取最小值
public static int getMin(int[] array) {
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
// 调用方法, 获取最大值
public static int getMax(int[] array) {
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (max < array[i]) {
max = array[i];
}
}
return max;
}
}
面向对象实现 :
public class ArrayTool {
// 调用方法, 将数组转成字符串
public static String arrayToString(int[] array) {
String temp = "[";
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
// 调用方法, 获取累加
public static int getSum(int[] array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
// 调用方法, 获取最小值
public static int getMin(int[] array) {
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
// 调用方法, 获取最大值
public static int getMax(int[] array) {
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (max < array[i]) {
max = array[i];
}
}
return max;
}
}
/*
* 需求 : 要完成数组的 最大值, 最小值, 求和, 打印 ...
的功能
*/
public class ArrayDemo1 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = {89, 34, 67, 88, 65, 23, 61};
int[] arr2 = {893, 324, 617, 838, 645, 233, 611};
// 2. 调用方法, 获取最大值
// 张三 (ArrayTool)
// public static 修饰符修饰的方法, 直接使用 `类名` 调用.
int max = ArrayTool.getMax(arr2);
System.out.println(max);
// 3. 调用方法, 获取最小值
int min = ArrayTool.getMin(arr);
System.out.println(min);
// 4. 调用方法, 获取累加和
int sum = ArrayTool.getSum(arr);
System.out.println(sum);
// 5. 调用方法, 将数组转成字符串
String result = ArrayTool.arrayToString(arr);
System.out.println(result);
}
}
2.类的设计 :
/*
* 如何设计一个类 :
*
* 1. 类名 见名知意
* 2. 属性 存储数据
* String color; “Red”, “Silver”, “gold”, “Yellow”
* int wheel; 2, 3 …
*
* 3. 行为 功能 / 方法 修饰符 返回值类型 方法名(参数列表) { }
* 以前 : 修饰符 -> public static
* 今天 : 修饰符 -> public
*
* 注意1 : 在被 static 修饰的方法中不能使用了类中的属性.
* 解决 : 如果要在该方法中使用属性, 那么该方法的修饰符千万不要加 static
.
*/
// 1. 类名
public class ElectricCar {
// 2. 属性 (存储数据)
String color;
int wheel;
// 3. 行为 (执行方法)
// Cannot make a static reference to the non-static field color
// 不能在静态环境中使用 `非静态的属性`.
public void run() {
System.out.println("color : " + color + ", whell : " + wheel + " run ...");
}
}
3.对象的创建,赋值,调用,与内存
4.局部变量与成员变量的区别
// 1. 类名
public class ElectricCar {
// 2. 属性 (存储数据)
String color;
int wheel;
// 3. 行为 (执行方法)
// Cannot make a static reference to the non-static field color
// 不能在静态环境中使用 `非静态的属性`.
public void run() {
System.out.println("color : " + color + ", whell : " + wheel + " run ...");
}
// 测试方法
public void test() {
// 局部变量
String color = "yellow";
int wheel = 8;
System.out.println("color : " + color + ", whell : " + wheel + " run ...");
}
}
5.属性封装的标准三步曲编写 (Student类)
/*
* 类的三要素 : 类名, 属性, 行为
*
* 相关的数据和相关的行为应该放在相关的类.
*
* Java 标准属性的封装 : (三步曲)
*
* ctrl + shift + s
*
* 1. 属性要私有化 修饰符: private 对内开放, 对外保护
* 2. 提供 setter 方法, 属性设置器. public void set属性名(参数类型 属性名) { this.属性名 = 属性名; }
* 3. 提供 getter 方法, 属性获取器. public 属性返回值类型 get属性名() { return 属性名; }
*
* 手动提供构造方法的格式 : (构造方法没有返回值类型, 因为构造方法就是返回当前类的对象)
*
* public 类名() {
*
* }
*/
// 1. 类名
public class Student {
// 2. 属性 (存储数据)
// 姓名
private String name;
// 年龄
private int age;
// 性别
private char gender;
// 3. 行为 (执行功能 / 方法) 在这里写的方法, 如果没有被调用, 是不可能会被执行了.
public void introduce() {
System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
}
// setter & getter
// name -> Jack
public void setName(String name) {
// 参数是以局部变量的形式存在的.
// String name = "Jack";
// The assignment to variable name has no effect name变量的赋值没有任何效果.
// 局部变量如果和成员变量同名, 优先访问 `局部变量`
// name = name; // 局部变量 = 局部变量;
// 成员变量 = 局部变量;
this.name = name;
}
public String getName() {
// 如果方法内部没有同名的局部变量, 程序会自动去寻找成员变量
return name;
}
// age
public void setAge(int age) {
// 判断
/*if (age < 0 || age > 120) {
this.age = 0;
} else {
this.age = age;
}*/
this.age = age;
}
public int getAge() {
return age;
}
// gender
public void setGender(char gender) {
/*if (gender == '女' || gender == '男') {
this.gender = gender;
} else {
this.gender = ' ';
}*/
this.gender = gender;
}
public char getGender() {
return gender;
}
}
测试类编写 :
public class StudentDemo1 {
public static void main(String[] args) {
// 1. 创建一个 Student 类的对象
Student stu1 = new Student();
// 2. 赋值 : 必须调用对应属性的 setter 方法实现复制
stu1.setName("Jack");
stu1.setAge(18);
stu1.setGender('男');
/*
stu1.name = "Jack";
stu1.age = 18;
stu1.gender = '男';
*/
// 3. 执行方法
stu1.introduce();
// 再创建一个学生
Student stu2 = new Student();
stu2.setName("Rose");
stu2.setAge(18);
stu2.setGender('女');
/*
stu2.name = "Rose";
stu2.age = -18;
stu2.gender = '女';
*/
stu2.introduce();
}
}
6.构造方法重载
问题思考 :
// () 表示调用方法, 括号中书写 `参数列表`.
// 类名(); 表示调用该类的构造方法.
// 构造方法 : 是类的一个特殊方法, 该方法只能在 new 该类时被调用.
// 其作用 : 在该对象创建的同时, 就是 new 该对象的同时, 为对象的属性进行 `显示初始化`.
// 说明 : 将两个步骤合并为一个步骤, 本来是先创建对象, 然后再给对象的属性赋值, 构造方法可以在创建对象的同时进行赋值.
// 变量 : int num = 10; 数组 : int[] arr = new int[]{10, 20, 30, 40, 50};
// 1. 创建一个 Student 类的对象
Student stu1 = new Student();
Student 类的构造方法设计 :
手动提供构造方法的格式 : (构造方法没有返回值类型, 因为构造方法就是返回当前类的对象)
public 类名() {
}
// 1. 类名
public class Student {
// 2. 属性 (存储数据)
// 姓名
private String name;
// 年龄
private int age;
// 性别
private char gender;
// 手动提供该类的构造方法
public Student(String name, int age, char gender) {
System.out.println("调用了 Student 类的有参构造方法, String, int, char ...");
// 赋值
this.name = name;
this.age = age;
this.gender = gender;
}
// 必须要提供一个无参的构造方法, 因为我们上面 `重载了构造方法`.
public Student() {
System.out.println("调用了 Student 类的无参构造方法 ...");
}
// 3. 行为 (执行功能 / 方法) 在这里写的方法, 如果没有被调用, 是不可能会被执行了.
public void introduce() {
System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
}
// setter & getter
……
}
测试类代码编写 :
public class StudentDemo2 {
public static void main(String[] args) {
// 需求 : 希望创建 Student 对象的同时为对象的属性进行初始化
Student stu1 = new Student("Jack", 18, '男');
stu1.introduce();
Student stu2 = new Student("Rose", 18, '女');
stu2.introduce();
}
}
7.this 原理图解
// 1. 类名
public class Student {
// 2. 属性 (存储数据)
// 姓名
private String name;
// 年龄
private int age;
// 性别
private char gender;
// 手动提供该类的构造方法
public Student(String name, int age, char gender) {
// System.out.println("调用了 Student 类的有参构造方法, String, int, char ...");
System.out.println("构造方法 this : " + this);
// 赋值
this.name = name;
this.age = age;
this.gender = gender;
}
// 必须要提供一个无参的构造方法, 因为我们上面 `重载了构造方法`.
public Student() {
// System.out.println("调用了 Student 类的无参构造方法 ...");
}
// 3. 行为 (执行功能 / 方法) 在这里写的方法, 如果没有被调用, 是不可能会被执行了.
public void introduce() {
System.out.println("introduct 方法 this : " + this);
System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
}
……
}
测试类 :
public class StudentDemo2 {
public static void main(String[] args) {
// 需求 : 希望创建 Student 对象的同时为对象的属性进行初始化
Student stu1 = new Student("Jack", 18, '男');
System.out.println(stu1);
stu1.introduce();
System.out.println("-------------------------------");
Student stu2 = new Student("Rose", 18, '女');
System.out.println(stu2);
stu2.introduce();
}
}
8.类名作为参数类型和返回值 (了解)
Teacher 类的设计 :
public class Teacher {
// 属性
// 行为
public void askQuestion(Student stu) {
// 1. 学生先进行自我介绍
stu.introduce();
// 2. 开始回答问题
System.out.println("开始回答问题 ... ");
}
// 类名也可以作为返回值类型 `静态方法`
// getInstance 获取本类的 `实例对象`
public static Teacher getInstance() {
Teacher teacher = new Teacher();
return teacher;
}
}
测试类编写 :
/*
* 类名可以作为参数及返回值类型 :
*/
public class StudentDemo1 {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("Jack");
stu1.setAge(30);
stu1.setGender('男');
stu1.introduce();
// 创建的同时进行初始化
Student stu2 = new Student("Rose", 18, '女');
stu2.introduce();
System.out.println("----------------------");
// 创建一个老师类
// Teacher teacher = new Teacher();
Teacher teacher = Teacher.getInstance();
teacher.askQuestion(stu2);
}
}
29.字符串 String
1.思考题 : 字面值与创建字符串
思考题 : 以下两个对象有什么区别?
String str = “abc”;
String str = new String(“abc”);
2.案例1 : 模拟用户登录
需求: 模拟登录,给三次机会,并提示还有几次.
演示效果如下 :
演示代码 :
public class LoginDemo2 {
public static void main(String[] args) {
String username = "admin";
String password = "123456";
// String username = new String("admin");
// String password = new String("123456");
String name = "admin";
String pwd = "123456";
/* 判断地址
if (username == name && password == pwd) {
System.out.println("登陆成功!");
} */
// 判断字符串内容
if (username.equals(name) && password.equals(pwd)) {
System.out.println("登陆成功!");
}
System.out.println("Over!");
}
}
案例实现 :
// Practice makes perfect. 熟能生巧
// 2.1 导包
import java.util.Scanner;
public class LoginDemo1 {
public static void main(String[] args) {
// 模拟登陆 :
// 1. 模拟正确的数据 (用户名 / 密码)
String username = "admin";
String password = "123456";
// 2.2 创建键盘录入对象
Scanner sc = new Scanner(System.in);
// 2.4 操作数据 (提示, 并接收用户的输入)
for (int i = 1; i <= 3; i++) {
System.out.println("亲, 请输入您的用户名 : ");
String name = sc.nextLine();
System.out.println("亲, 请输入您的密码 : ");
String pwd = sc.nextLine();
// 3. 后期处理 (判断用户名和密码是否正确)
if (username.equals(name) && password.equals(pwd)) {
System.out.println("登陆成功!");
// 跳出循环
break;
} else {
// 登陆失败
if (i == 3) {
System.out.println("登录失败, 您的用户已经被锁定, 联系你妈咪.");
// 跳出
break;
}
System.out.println("用户名或密码错误, 请重新登录, 您还有"+(3-i)+"次机会.");
}
}
// 2.3 关闭资源
sc.close();
}
}
3.案例2 : 忽略大小写判断用户的选择
演示 : 您确认要删除吗 ?
// 1.1 导包
import java.util.Scanner;
public class ConfirmDemo3 {
public static void main(String[] args) {
// 您确认要删除吗 ???
System.out.println("亲, 您真的要抛弃我吗? 干掉我吗? y (确认) / n (取消) : ");
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户输入)
String choice = sc.nextLine();
// 1.3 关闭资源
sc.close();
// 2. 后期处理
if ("y".equals(choice) || "Y".equals(choice)) {
System.out.println("确认.");
} else {
System.out.println("取消.");
}
}
}
4.案例3 : 统计大写, 小写, 数字字符出现的次数
演示效果如下 :
// 1.1 导包
import java.util.Scanner;
public class GetCaseAndNumberDemo4 {
public static void main(String[] args) {
// 案例3 : 统计大写, 小写, 数字字符出现的次数
// 1.2 创建一个 键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个字符串 :");
String str = sc.nextLine();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (统计大写, 小写, 数字字符出现的次数)
// duia8976sdfY%%%%SDFAS89yy6u
// 2.3 定义三个框变量
int upperCase = 0;
int lowerCase = 0;
int number = 0;
// 2.1 遍历字符串
// 问题1 : 字符串的长度如何获取 ??? String 类. length();
for (int i = 0; i < str.length(); i++) {
// 问题2 : 字符串中的每一个字符如何获取呢 ??? String 类. char
char ch = str.charAt(i);
// System.out.println(ch);
// 2.2 判断取出的每一个字符
if (ch >= 'a' && ch <= 'z') {
// 小写
lowerCase++;
} else if (ch >= 'A' && ch <= 'Z') {
// 大写
upperCase++;
} else if (ch >= '0' && ch <= '9') {
// 数字
number++;
}
}
// 3. 查看结果
System.out.println("upperCase = " + upperCase);
System.out.println("lowerCase = " + lowerCase);
System.out.println("number = " + number);
}
}
5.案例4 : 字符串切割,截取与大小写转换
演示效果如下 :
// 1.1 导包
import java.util.Scanner;
public class WordsDemo5 {
public static void main(String[] args) {
// 案例4 : 字符串切割,截取与大小写转换
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接受用户的输入)
System.out.println("亲, 请输入一个英文单词字符串, 并使用空格分隔 :");
String str = sc.nextLine();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (hApPy sad woNdeRful beAUtiful UGly hunGRy gAy)
// {"happy", "sad", "wonderful", "beautiful", "ugly", "hungry", "gay"}
// 问题1 : 如何从一个整个的字符串中 `切割 / split` 每一块内容 ???
String[] words = str.split(" +"); // + 以一个或多个空格作为 `切割符`
// 遍历数组
for (int i = 0; i < words.length; i++) {
// System.out.println(words[i]);
String word = words[i];
// 问题2 : 如果将一个字符串首字符变成大写呢 ??? happy
// char ch = word.charAt(0); // char 是基本数据类型, 没有转换为大写的功能.
// 解决2 : 将一个字符串中的固定内容字符进行 `截取 / substring`.
String head = word.substring(0, 1); // substring(头, 尾); 包头不包尾.
// System.out.println(head);
String end = word.substring(1);
// System.out.println(end);
// 问题3 : 如何将一个字符串转换为大写 ??? toUpperCase();
String uppperHead = head.toUpperCase();
// System.out.println(uppperHead);
String lowerEnd = end.toLowerCase();
// System.out.println(lowerEnd);
// 拼接字符串
String newWord = uppperHead + lowerEnd;
System.out.println(newWord);
}
}
}
6.案例5 : 反转字符串
/*
* 字符串反转
* 举例:键盘录入 “abcdefg”
* 输出结果: “gfedcba”
*
* 分析:
* 1:键盘录入一个字符串
* 2:写方法实现字符串的反转
* 方式一:把字符串倒着遍历,得到的每一个字符拼接成字符串。
* 方式二:把字符串转换为字符数组,然后对字符数组进行反转,最后在把字符数组转换为字符串
* 3:调用方法
* 4:输出结果
*/
演示效果如下 :
实现方式一 : 倒序遍历 + 拼接字符串
// 1.1 导包
import java.util.Scanner;
public class ReverseStringDemo6 {
public static void main(String[] args) {
// 案例5 : 反转字符串
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个字符串 :");
String str = sc.nextLine();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (反转)
String reverseStr = reverseString2(str);
System.out.println(reverseStr);
}
// 定义一个方法, 反转传入参数的字符串
public static String reverseString1(String str) {
// str -> abcdefg
// 1. 定义一个临时字符串, 用于拼接
String temp = "";
// 2. 倒序遍历字符串
for (int i = str.length() - 1; i >= 0; i--) {
// 3. 取出当前遍历的字符
char ch = str.charAt(i);
// System.out.println(ch);
// 4. 拼接
temp += ch;
}
// 5. 返回
return temp;
}
}
实现方式二 : 使用字符串提供的方法
// 1.1 导包
import java.util.Scanner;
public class ReverseStringDemo6 {
public static void main(String[] args) {
// 案例5 : 反转字符串
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入一个字符串 :");
String str = sc.nextLine();
// 1.3 关闭资源
sc.close();
// 2. 后期处理 (反转)
String reverseStr = reverseString2(str);
System.out.println(reverseStr);
}
// 定义一个方法, 反转传入参数的字符串
public static String reverseString2(String str) {
// 问题1 : 如何将一个字符串转换为一个 `字符数组`. toCharArray();
char[] charArray = str.toCharArray();
// System.out.println(charArray); // 字符数组打印可以直接输出内容. 而不是地址.
/*for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}*/
// 问题2 : 如何反转一个字符数组 ???
for (int start = 0, end = charArray.length - 1; start < end; start++, end--) {
// 交换
char temp = charArray[start];
charArray[start] = charArray[end];
charArray[end] = temp;
}
// System.out.println(charArray);
// 问题3 : 如何将一个字符数组转换成一个字符串 ???
// String s = charArray.toString(); // 行不通 : [C@3c3e4344
String s = new String(charArray);
// System.out.println(s);
return s;
}
}
30.字符串缓冲区 StringBuilder
1.capacity 和 length 容量与长度
public class StringBuilderDemo {
public static void main(String[] args) {
/*
* capacity 容量
* length 长度
*/
// 1. 创建一个字符串缓冲区
StringBuilder sb = new StringBuilder();
// 容量
int capacity = sb.capacity();
// 长度
int length = sb.length();
System.out.println("capacity = " + capacity);
System.out.println("length = " + length);
// append 拼接
sb.append("hello");
sb.append("welcome");
sb.append("world");
// 容量
capacity = sb.capacity();
// 长度
length = sb.length();
System.out.println("capacity = " + capacity);
System.out.println("length = " + length);
}
}
2.append 拼接 (链式编程)
可以给容器中添加任何的数据。每次都是给容器中最后位置添加。
public class StringBuilderDemo2 {
public static void main(String[] args) {
// 1. 创建一个字符串缓冲区
StringBuilder sb1 = new StringBuilder();
// append 方法有返回值类, 为什么不接收 ? 因为该方法返回的就是 `自己本身的地址`.
sb1.append("hello");
sb1.append("world");
sb1.append(true);
sb1.append(998);
sb1.append(88.88f);
System.out.println(sb1);
// 2. 拼接的方式二 : 链式编程
StringBuilder sb2 = new StringBuilder();
sb2.append("hello").append("world").append(true).append(998).append(88.88f);
System.out.println(sb2);
/*
// 2. 拼接 (自娱自乐)
StringBuilder sb2 = sb1.append("hello");
StringBuilder sb3 = sb2.append("world"); // toString(); Object 类
System.out.println(sb1);
System.out.println(sb2);
System.out.println(sb3);
System.out.println(sb1 == sb2);
System.out.println(sb1 == sb3);
*/
}
}
3.案例1 : 把int数组拼接成一个字符串
需求 : 使用字符串缓冲区完成该案例 :
public class StringBuilderDemo3 {
public static void main(String[] args) {
// 把int数组拼接成一个字符串
int[] arr = {10, 20, 30, 40, 50};
String result = arrayToString(arr);
System.out.println(result);
}
// 方式二 : 使用字符串缓冲区实现 :
public static String arrayToString(int[] array) {
// 需求 : 在字符串缓冲区中完成结果的拼接, 最后返回拼接完成的字符串给调用者
// 1. 创建一个字符串缓冲区对象
StringBuilder sb = new StringBuilder("[");
// 2. 遍历数组
for (int i = 0; i < array.length - 1; i++) {
sb.append(array[i]).append(", ");
}
// 3. 在外部拼接数组中的最后一个元素
sb.append(array[array.length - 1]).append("]"); // i -> 最后一个元素的下标 array.length - 1
// 4. 返回结果
return sb.toString();
}
// 字符串常量实现 :
public static String arrayToString1(int[] array) {
String temp = "[";
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
temp = temp + array[i] + "]";
} else {
temp = temp + array[i] + ", ";
}
}
return temp;
}
}
4.案例2 : 利用StringBuilder完成字符串反转
public class StringBuilderDemo4 {
public static void main(String[] args) {
// 需求 : 利用StringBuilder完成字符串反转
// 说明1 : String 类不具备反转的功能.
String str = "abcdefg";
// 调用方法
String reverseString = reverseString(str);
System.out.println(reverseString);
/*
// str.reverse(); 错误的.
// 说明2 : StringBuilder 字符串缓冲区具备 `反转` 的功能.
StringBuilder sb = new StringBuilder("abcdefg");
sb.reverse();
System.out.println(sb);
*/
}
/*
* 需求 : 定义一个方法, 实现字符串的反转
* 明确1 : 返回值类型 String 类型
* 明确2 : 参数列表 String 类型
*/
public static String reverseString(String str) {
// 1. 创建一个字符串缓冲区, 缓冲区中的默认内容为传入的数据 str
StringBuilder sb = new StringBuilder(str);
// 2. 使用字符串缓冲区对象调用 reverse() 方法, 实现缓冲区中的字符串内容反转
sb.reverse();
// 3. 使用字符串缓冲区对象调用 toString() 方法, 返回字符串常量
String reverseStr = sb.toString();
return reverseStr;
}
}
5.案例3 : 拼接一个字符串是否为对称字符串
例如”abc”不是对称字符串,”aba”、”abba”、”aaa”、”mnanm”是对称字符串
知识点 : StringBuilder和String 相互转换.
演示效果如下 :
import java.util.Scanner;
public class StringBuilderDemo5 {
public static void main(String[] args) {
// 需求 : 定义一个方法, 获取键盘录入的字符串, 判断是否为 `对称字符串`.
Scanner sc = new Scanner(System.in);
System.out.println("亲, 请输入一个字符串 :");
String str = sc.nextLine();
sc.close();
// 调用方法, 判断字符串是否为对称字符串
boolean result = symmetricalString(str);
System.out.println(result);
}
/*
* 需求 : 判断是否为 `对称字符串`
* 明确1 : 返回值类型 boolean 类型
* 明确2 : 参数列表 String 类型
*/
public static boolean symmetricalString(String str) {
// 1. 将传入的字符串反转, 反转需要使用 字符串缓冲区.
StringBuilder sb = new StringBuilder(str);
sb.reverse();
// 2. 将字符串缓冲区转成字符串常量
String reverseStr = sb.toString();
// 3. 使用两个字符串常量进行相等判断.
// boolean result = str.equals(sb); // 错误的! str 是字符串常量, sb 是堆区中的字符串缓冲区.
boolean result = str.equals(reverseStr); // 正确的!
// 4. 返回结果
return result;
}
}
31.对象数组案例
需求 : 创建一个学生数组,存储三个学生对象并遍历.
// 1. 类名
public class Student {
// 2. 属性 (存储数据)
private String name;
private int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() { }
// 3. 行为 (方法)
// setter & getter
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;
}
}
代码演示一 :
// 1.1 导包 ctrl + shift + o
import java.util.Scanner;
public class ArrayDemo1 {
public static void main(String[] args) {
/*
* 对象数组 : 数组最严重的问题就是 `长度固定`.
*
* int[], char[], String[] ...
*
* 需求1 : 定义一个数组, 用于存储 `学生` 对象. Student[] 姓名, 年龄
* 需求2 : 创建一个学生数组,存储三个学生对象并遍历.
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 4.1 创建一个学生数组
Student[] stus = new Student[3];
// 2. 循环 3 次
for (int i = 0; i < 3; i++) {
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第"+(i+1)+"个学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入第"+(i+1)+"个学生的年龄 : ");
// int age = sc.nextInt();
int age = Integer.parseInt(sc.nextLine());
// 3. 将接收的 `姓名, 年龄` 封装成一个 `Student` 对象
Student stu = new Student(name, age);
// 4.2 将封装完成的学生对象存储到一个 `学生数组` 中
stus[i] = stu;
}
// 1.3 关闭资源
sc.close();
// 5. 遍历数组, 查看数组中的每一个学生对象
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
System.out.println(stu.getName() + " : " + stu.getAge());
}
}
}
代码演示二 :
// 1.1 导包 ctrl + shift + o
import java.util.Scanner;
public class ArrayDemo2 {
public static void main(String[] args) {
/*
* 对象数组 : 数组最严重的问题就是 `长度固定`.
* 解决方案 : 数组列表 ArrayList 类. 底层就是一个 `长度可变的数组`.
*
* int[], char[], String[] ...
*
* 需求1 : 定义一个数组, 用于存储 `学生` 对象. Student[] 姓名, 年龄
* 需求2 : 创建一个学生数组,存储三个学生对象并遍历.
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 4.1 创建一个学生数组
// Variable must provide either dimension expressions (维度表达式) or an array initializer
// stus 变量必须要提供长度或数组的初始化. (二选一)
Student[] stus = new Student[3];
// 定义一个存储的下标
int index = 0;
// 2. 循环 3 次
while (true) {
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入学生的年龄 : ");
// int age = sc.nextInt();
int age = Integer.parseInt(sc.nextLine());
// 3. 将接收的 `姓名, 年龄` 封装成一个 `Student` 对象
Student stu = new Student(name, age);
// 4.2 将封装完成的学生对象存储到一个 `学生数组` 中
stus[index] = stu;
index++;
// 跳出循环
System.out.println("亲, 还需要继续添加吗 ? y (确定) / n (取消) : ");
String choice = sc.nextLine();
// 判断选择 "y".equalsIgnoreCase(choice) == false -> !"y".equalsIgnoreCase(choice)
if (!"y".equalsIgnoreCase(choice)) {
break;
}
}
// 1.3 关闭资源
sc.close();
// 5. 遍历数组, 查看数组中的每一个学生对象
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
System.out.println(stu.getName() + " : " + stu.getAge());
}
}
}
32.集合 – ArrayList (数组列表)
1.需求 : ArrayList 存储自定义对象并遍历.
// 1.1 导包 ctrl + shift + o
import java.util.Scanner;
import java.util.ArrayList;
public class ArrayListDemo3 {
public static void main(String[] args) {
/*
* 对象数组 : 数组最严重的问题就是 `长度固定`.
* 解决方案 : 数组列表 ArrayList 类. 底层就是一个 `长度可变的数组`.
*
* int[], char[], String[] ...
*
* 需求1 : 定义一个数组, 用于存储 `学生` 对象. Student[] 姓名, 年龄
* 需求2 : 创建一个学生数组,存储三个学生对象并遍历.
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 4.1 创建一个学生 `数组列表 / 集合`
// 集合是一个体系 (ArrayList 仅仅是集合体系中的一个类而已)
ArrayList<Student> stus = new ArrayList<Student>();
// 2. 循环 3 次
while (true) {
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入学生的年龄 : ");
// int age = sc.nextInt();
int age = Integer.parseInt(sc.nextLine());
// 3. 将接收的 `姓名, 年龄` 封装成一个 `Student` 对象
Student stu = new Student(name, age);
// 4.2 将封装完成的学生对象存储到一个 `学生数组列表` 中
stus.add(stu);
// 跳出循环
System.out.println("亲, 还需要继续添加吗 ? y (确定) / n (取消) : ");
String choice = sc.nextLine();
// 判断选择 "y".equalsIgnoreCase(choice) == false -> !"y".equalsIgnoreCase(choice)
if (!"y".equalsIgnoreCase(choice)) {
break;
}
}
// 1.3 关闭资源
sc.close();
// 5. 遍历数组, 查看数组中的每一个学生对象
for (int i = 0; i < stus.size(); i++) {
Student stu = stus.get(i);
System.out.println(stu.getName() + " : " + stu.getAge());
}
}
}
2.ArrayList增删改查方法
需求 :
1.向集合容器中添加如下字符串. “柳岩”,”范冰冰”,”李冰冰”,”刘亦菲”,”汤唯”
2.遍历, 并查找李冰冰, 然后调用方法删除 “李冰冰”.
3.将 “汤唯”改为 “赵薇”.
import java.util.ArrayList;
public class ArrayListDemo1 {
public static void main(String[] args) {
/*
1. 向集合容器 (ArrayList) 中添加如下字符串. “柳岩”,”范冰冰”,”李冰冰”,”刘亦菲”,”汤唯”
2. 遍历, 并查找李冰冰, 然后调用方法删除 “李冰冰”.
3. 将 “汤唯”改为 “赵薇”.
*/
// 1. 创建一个集合容器 (ArrayList 数组列表)
ArrayList<String> list = new ArrayList<String>();
// 2. 添加
list.add("柳岩");
list.add("范冰冰");
list.add("李冰冰");
list.add("刘亦菲");
list.add("汤唯");
// 3. 说明 : 如果集合中存储的是字符串内容, 直接输出集合, 即可以看到所有的字符串数据.
System.out.println(list);
// 4. 遍历
for (int i = 0; i < list.size(); i++) {
// 并查找李冰冰, 然后调用方法删除 “李冰冰”.
// 5. 取出当前遍历的字符串对象
String name = list.get(i);
// 6. 判断
if (name.equals("李冰冰")) {
// 删除 “李冰冰”
list.remove(i);
} else if (name.equals("汤唯")) {
// 将 “汤唯”改为 “赵薇”
list.set(i, "赵薇");
}
}
System.out.println(list);
}
}
import java.util.ArrayList;
public class ArrayListDemo2 {
public static void main(String[] args) {
// 给定一个字符串数组:String[] names = {"张三丰", "宋远桥", "张无忌", "殷梨亭", "张翠山", "莫声谷"};
// 将数组中的元素添加到集合中,并把所有姓张的人员打印到控制台上.
// 1. 定了一个字符串数组
String[] names = {"张三丰", "宋远桥", "张无忌", "殷梨亭", "张翠山", "莫声谷"};
// 2. 将数组中的元素添加到集合中
// 2.1 定义一个集合 ArrayList 数组列表集合
ArrayList<String> list = new ArrayList<String>();
// 2.2 遍历 names 数组, 获取每一个元素, 然后将元素添加到数组列表集合中
for (int i = 0; i < names.length; i++) {
String name = names[i];
// 添加到集合中
list.add(name);
}
// 3. 把所有姓张的人员打印到控制台上
// 3.1 遍历集合, 获取集合中当前遍历的元素
for (int i = 0; i < list.size(); i++) {
// 取出
String name = list.get(i);
// 3.2 判断是否姓 `张` name.charAt(0) startsWith(字符串) endsWith(字符串) 字符串
// charAt, startsWith, endsWith, contains 字符串
// add, get, set, remove, size 集合中
// if (name.charAt(0) == '张') {
if (name.startsWith("张") || name.endsWith("桥")) {
// 3.3 如果条件成立, 输出到控制台
System.out.println(name);
}
}
}
}
4.数组与集合 (判断元素是否包含,并从集合中删除)
给定一个字符串数组:String[] names = {“柳岩”, “范冰冰”, “李冰冰”, “刘亦菲”, “赵薇”, “杨幂”, “冰激凌”, “雪糕冰”}; 将数组中的元素添加到集合中,并把所有名称中包含 冰
的元素从集合中删除, 最后查看集合中的元素.
import java.util.ArrayList;
public class ArrayListDemo3 {
public static void main(String[] args) {
// 给定一个字符串数组:String[] names = {"柳岩", "范冰冰", "李冰冰", "刘亦菲", "赵薇", "杨幂", "冰激凌", "雪糕冰"};
// 将数组中的元素添加到集合中,并把所有名称中包含 `冰` 的元素从集合中删除, 最后查看集合中的元素.
// 1. 定了一个字符串数组
String[] names = {"柳岩", "范冰冰", "李冰冰", "刘亦菲", "赵薇", "杨幂", "冰激凌", "雪糕冰"};
// 2. 将数组中的元素添加到集合中
// 2.1 定义一个集合 ArrayList 数组列表集合
ArrayList<String> list = new ArrayList<String>();
// 2.2 遍历 names 数组, 获取每一个元素, 然后将元素添加到数组列表集合中
for (int i = 0; i < names.length; i++) {
String name = names[i];
// 添加到集合中
list.add(name);
}
System.out.println(list);
// 3. 遍历集合, 并把所有名称中包含 `冰` 的元素从集合中删除, 最后查看集合中的元素.
for (int i = 0; i < list.size(); i++) {
// 获取
String name = list.get(i);
// 判断包含 : contains
if (name.contains("冰")) {
// 删除
list.remove(i);
// 注意点 : 遍历集合的同时, 如果要删除集合中的元素, 一定要让下标做一次 `自减` 操作.
i--;
}
}
// 4. 查看
System.out.println(list);
}
}
33.IO_输入与输出流 Input Output
需求1 : 向文件中写入字符串数据.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws IOException {
// 需求1 : 向文件中写入字符串数据.
// 1. 创建一个 `高效的缓冲区文件写入对象 / BufferedWriter`
BufferedWriter writer = new BufferedWriter(new FileWriter("demo.txt"));
// 2. 黄金三步曲 (写入, 换行, 刷新)
// 写入
// How are you doing ? 你过的好吗 ? What are you doing ?
writer.write("How old are you?");
// 换行
writer.newLine();
// 刷新
writer.flush();
writer.write("怎么老是你呀.");
writer.newLine();
writer.flush();
// 3. 关闭资源
writer.close();
System.out.println("执行完成!");
}
}
需求2 : 将集合中的学生对象信息写入到文件中.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class BufferedWriterDemo2 {
public static void main(String[] args) throws IOException {
ArrayList<Student> stus = new ArrayList<Student>();
Student stu1 = new Student("itcast_001", "Jack", 30, "上海");
Student stu2 = new Student("itcast_002", "Rose", 18, "叙利亚");
stus.add(stu1);
stus.add(stu2);
// 需求 : 集合中的学生对象写入到文件中.
// 1. 创建一个 `高效的缓冲区文件写入对象`
BufferedWriter writer = new BufferedWriter(new FileWriter("stus.txt"));
// 2. 黄金三步曲 (写入, 换行, 刷新)
// 2.1 遍历集合
for (int i = 0; i < stus.size(); i++) {
// 2.2 取出当前遍历的学生对象
Student stu = stus.get(i);
// 2.3 查看信息
// System.out.println(stu.getId() + " : " + stu.getName() + " : " + stu.getAge() + " : " + stu.getAddress());
// 2.4 创建一个字符串缓冲区对象
StringBuilder sb = new StringBuilder();
// itcast_001,Jack,30,上海
sb.append(stu.getId()).append(",").append(stu.getName()).append(",")
.append(stu.getAge()).append(",").append(stu.getAddress());
// 2.5 黄金三步曲
// 写入
writer.write(sb.toString());
// 换行
writer.newLine();
// 刷新
writer.flush();
}
// 3. 关闭资源
writer.close();
System.out.println("执行完成!");
}
}
需求1 : 读取文件中的字符串数据.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo1 {
public static void main(String[] args) throws IOException {
// 需求1 : 读取文件中的字符串数据.
// 1. 创建一个 `高效的缓冲区文件读取对象 / BufferedReader`
BufferedReader reader = new BufferedReader(new FileReader("demo.txt"));
// 2. 一行一行读 (readLine)
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 3. 关闭资源
reader.close();
/*int index = -1;
if (index != -1) {
}*/
/*
String line = reader.readLine();
System.out.println(line);
line = reader.readLine();
System.out.println(line);
line = reader.readLine();
System.out.println(line);
line = reader.readLine();
System.out.println(line);
line = reader.readLine();
System.out.println(line);
line = reader.readLine();
System.out.println(line);
line = reader.readLine(); // null
System.out.println(line);
*/
}
}
需求2 : 读取文件中的字符串数据封装为学生对象, 并将完成数据封装的学生对象存储到集合中.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class BufferedReaderDemo2 {
public static void main(String[] args) throws IOException {
// 需求 : 将 stus.txt 文件中的数据读取到 ArrayList 集合中, 以 Student 对象进行存储.
ArrayList<Student> stus = new ArrayList<Student>();
// 1. 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("stus.txt"));
// 2. 一行一行读
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line);
// line -> itcast_001,Jack,30,上海
// 请问 : 字符串切割 split
// split -> {"itcast_001", "Jack", "30", "上海"}
String[] split = line.split(",");
String id = split[0];
String name = split[1];
String age_str = split[2];
int age = Integer.parseInt(age_str);
String address = split[3];
// System.out.println(id + " : " + name + " : " + age + " : " + address);
// 将取出的数据封装成每一个学生对象
Student stu = new Student(id, name, age, address);
// 将封装完成数据的学生存储到学生集合中
stus.add(stu);
}
// 3. 关闭资源
reader.close();
// 遍历
System.out.println(stus);
}
}
3.案例一 :
案例一 :
1.在项目根路径下创建文件data.txt,data.txt中存放编程语言数据如下:
Java
PHP
IOS
Python
(data.txt文件和文件中的数据可以手动创建和录入,无需由程序生成)
1.编写一个程序,将data.txt文件中的编程语言全部读取到ArrayList中,随机从集合中获取一个语言作为年度最佳语言,并在控制台打印输出,
演示格式如下:
// 3.1 导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
public class PracticeDemo1 {
public static void main(String[] args) throws IOException {
/*
案例一 :
1.在项目根路径下创建文件data.txt,data.txt中存放编程语言数据如下:
Java
PHP
IOS
Python
(data.txt文件和文件中的数据可以手动创建和录入,无需由程序生成)
1. 编写一个程序,将data.txt文件中的编程语言全部读取到ArrayList中,随机从集合中获取一个语言作为年度最佳语言,并在控制台打印输出,
演示格式如下:
*/
// 1. 创建一个集合, 存储字符串数据
ArrayList<String> list = new ArrayList<String>();
// 2.1 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
// 2.2 一行一行读
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line);
// 将读取的字符串存储到集合中
list.add(line);
}
// 2.3 关闭资源
reader.close();
// 3.2 创建一个随机数对象
Random random = new Random();
// 3.3 操作数据 (生成一个随机数 nextInt(n) n -> 集合的长度 )
int index = random.nextInt(list.size());
// 4. 根据下标从集合中获取数据, 最后输出
String language = list.get(index);
System.out.println("本年度最佳语言为 : " + language);
}
}
4.案例二 :
案例二 :
1、项目根路径创建1个文件data2.txt,用于存放程序写入的字符串 (文件的创建可以手动创建,无需使用程序创建)
2. 使用循环语句,循环写入4个键盘录入的字符串到文件data2.txt中,写入字符串之前需要提示当前录入的是第几个字符串
演示格式具体如下:图1为输入演示,图2为data2.txt存入字符串的格式.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class PracticeDemo2 {
public static void main(String[] args) throws IOException {
/*
案例二 :
1、项目根路径创建1个文件data2.txt,用于存放程序写入的字符串 (文件的创建可以手动创建,无需使用程序创建)
2. 使用循环语句,循环写入4个键盘录入的字符串到文件data2.txt中,写入字符串之前需要提示当前录入的是第几个字符串
演示格式具体如下:图1为输入演示,图2为data2.txt存入字符串的格式.
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 3.1 创建一个高效的缓冲区文件写入对象
BufferedWriter writer = new BufferedWriter(new FileWriter("data2.txt"));
// 2. 循环执行 4 次
for (int i = 1; i <= 4; i++) {
// 1.4 操作数据 (提示, 并接收用户的输入) abc
System.out.println("亲, 请输入第"+i+"行数据 :");
String line = sc.nextLine();
// 3.2 黄金三步曲 (写入, 换行, 刷新) 第1行: abc
String str = "第"+i+"行: " + line;
writer.write(str);
writer.newLine();
writer.flush();
}
// 1.3 关闭资源
sc.close();
// 3.3 关闭资源
writer.close();
System.out.println("执行完成!");
}
}
5.案例三 :
案例三 :
1)项目根路径有2个文件,data3.txt和result.txt,data3.txt存放一行字符串:” goOd gooD stUdy dAy dAy up” (data3.txt的文件内容可以手动输入)
2)将data3.txt文件中每个单词的首字母转换成大写其余都为小写字母,将转换后的字符串写入项目根路径的result.txt文件中
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class PracticeDemo3 {
public static void main(String[] args) throws IOException {
/*
案例三 :
1)项目根路径有2个文件,data3.txt和result.txt,data3.txt存放一行字符串:” goOd gooD stUdy dAy dAy up”
(data3.txt的文件内容可以手动输入)
2)将data3.txt文件中每个单词的首字母转换成大写其余都为小写字母,将转换后的字符串写入项目根路径的result.txt文件中
*/
// 先读后写
// 1.1 创建一个高效的缓冲区读取对象
BufferedReader reader = new BufferedReader(new FileReader("data3.txt"));
// 6.1 定义一个字符串缓冲区, 用于拼接转换完成的单词
StringBuilder sb = new StringBuilder();
// 1.2 一行一行读
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line);
// line -> goOd gooD stUdy dAy dAy up
// 2 切割
String[] split = line.split(" +");
// 3. 遍历字符串数据
for (int i = 0; i < split.length; i++) {
// System.out.println(split[i]);
/*
goOd
gooD
stUdy
dAy
dAy
up
*/
// 4. 截取首字母
String str = split[i];
String head = str.substring(0, 1);
String end = str.substring(1);
// 5. 头大尾小
String word = head.toUpperCase() + end.toLowerCase();
// System.out.println(word);
// 6.2 记录每一个转换成功的单词
sb.append(word).append(" ");
}
}
// 1.3 关闭资源
reader.close();
// 6.3 写入操作
// 6.3.1 创建一个高效的缓冲区写入对象
BufferedWriter writer = new BufferedWriter(new FileWriter("result.txt"));
// 6.3.2 黄金三步曲 (写入, 换行, 刷新)
writer.write(sb.toString());
writer.newLine();
writer.flush();
// 6.3.3 关闭资源
writer.close();
System.out.println("执行成功!");
}
}
6.案例四 :
案例四 :
1.在项目根路径下创建文件data4.txt,data4.txt中存放的数据如下:
isn%96H@
A#m18-Zo
Us8-Bur@?
(data4.txt文件和文件中的数据可以手动创建和录入,无需由程序生成)
2.编写一个程序,获取data4.txt文件中所有的数字,按照以下格式,在控制台打印输出。演示格式如下:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class PracticeDemo4 {
public static void main(String[] args) throws IOException {
/*
案例四 :
1.在项目根路径下创建文件data4.txt,data4.txt中存放的数据如下:
isn%96H@
A#m18-Zo
Us8-Bur@?
(data4.txt文件和文件中的数据可以手动创建和录入,无需由程序生成)
2.编写一个程序,获取data4.txt文件中所有的数字,按照以下格式,在控制台打印输出。演示格式如下:
*/
// 1. 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("data4.txt"));
System.out.print("文件中的数字为: ");
// 2. 一行一行读
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line);
// line -> isn%96H@
// 4. 遍历当前读取的 line 字符串
for (int i = 0; i < line.length(); i++) {
// 5. 获取字符串中的每一个字符
char ch = line.charAt(i);
// 6. 判断
if (ch >= '0' && ch <= '9') {
System.out.print(ch);
}
}
}
// 3. 关闭资源
reader.close();
}
}
7.案例五 :
案例五 :
根据以下要求依次实现相关功能
a. 自定义学生类(Student),属性有:学号(String),姓名(String),成绩(double),生成对应的set/get方法以及有参构造;
b. 提示用户按照:“学号,姓名,成绩”的格式输入5组相关数据(每个数据要求用逗号隔开,接收时无需做纠错处理);
c. 根据用户输入的数据利用有参构造生成对应的student对象,然后将这些对象存入ArrayList中;
d. 遍历集合,将成绩在80分以上的学生相关信息存入当前项目根目录sj.txt(升级名单)中;
比如:
(控制台录入示例如下) (sj.txt示例如下)
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
public class PracticeDemo5 {
public static void main(String[] args) throws IOException {
/*
案例五 :
根据以下要求依次实现相关功能
a. 自定义学生类(Student),属性有:学号(String),姓名(String),成绩(double),生成对应的set/get方法以及有参构造;
b. 提示用户按照:“学号,姓名,成绩”的格式输入5组相关数据(每个数据要求用逗号隔开,接收时无需做纠错处理);
c. 根据用户输入的数据利用有参构造生成对应的student对象,然后将这些对象存入ArrayList<Student>中;
d. 遍历集合,将成绩在80分以上的学生相关信息存入当前项目根目录sj.txt(升级名单)中;
比如:
*/
// 1.2 创建一个键盘录入对象
Scanner sc = new Scanner(System.in);
// 4.1 定义一个集合, 用户存储学生对象
ArrayList<Student> list = new ArrayList<Student>();
// 2. for 循环执行 5 次.
for (int i = 1; i <= 5; i++) {
// 1.4 操作数据 (提示, 并接收用户的输入)
System.out.println("亲, 请输入第"+i+"个学生的学号,姓名,成绩, 并使用逗号分隔 :");
String line = sc.nextLine();
// line -> 1,zs,80
// 1.5 切割字符串
String[] split = line.split(",");
String id = split[0];
String name = split[1];
String score_str = split[2];
// 请问 : 如何将一个字符串转换为 double 类型呢 ??? Integer, Double, Float ...
double score = Double.parseDouble(score_str);
// 3. 将用户输入的数据, 封装成一个学生对象
Student stu = new Student(id, name, score);
// 4.2 将封装完成数据的学生对象存储到集合中
list.add(stu);
}
// 1.3 关闭资源
sc.close();
// 8.1 创建一个高效的缓冲区文件写入对象
BufferedWriter writer = new BufferedWriter(new FileWriter("sj.txt"));
// 后期处理
// 5. 遍历集合
for (int i = 0; i < list.size(); i++) {
// 6. 取出当前遍历的学生
Student stu = list.get(i);
// 7. 判断学生的分数
if (stu.getScore() > 80) {
// 需求 : 条件成立, 需要将该学生的信息写入到 sj.txt 文件中
// 8.2 黄金三步曲
// 学号: 3 姓名: ww 成绩: 90.0
String str = "学号: "+stu.getId()+" 姓名: "+stu.getName()+" 成绩: "+stu.getScore();
writer.write(str);
writer.newLine();
writer.flush();
}
}
// 8.3 关闭资源
writer.close();
}
}
基础综合案例 : 学生管理系统
1.功能 : 主界面搭建
/*
* 问题1 : 为什么我们的程序结束了 ??? main方法结束, 程序就结束了.
* 解决1 : 如果让程序不要结束. 循环 (不确定次数, 请使用 while (true) 死循环)
*
* 问题2 : 程序再也停不住了, 怎么办 ???
* 解决2 : 键盘录入对象, 等待用户输入. 1. 导包 2. 创建键盘录入对象 3. 关闭资源 4. 操作数据(提示, 并接收用户的输入)
*
* 问题3 : 如果使用键盘录入的 nextInt() 接收输入, 此时, 用户输入字符串, 程序就会异常崩溃. 无法控制 ???
* 解决3 : 不要使用 nextInt() 接收, 而应该使用 nextLine() 接收.
*
* 问题4 : 接收到用户的输入之后, 需要做什么.
* 解决4 : 使用 switch 语句对用户的操作进行判断.
*
* 问题5 : 如果退出 Java 虚拟机程序.
* 解决5 : System.exit(0); 0 表示正常退出.
*/
// 1. 导包
import java.util.Scanner;
public class StudentManageSystemDemo {
public static void main(String[] args) {
// 2. 创建键盘录入对象
Scanner sc = new Scanner(System.in);
// 死循环
while (true) {
System.out.println("----- 欢迎来到学生管理系统 -----");
System.out.println("1: 查看所有学生");
System.out.println("2: 添加学生");
System.out.println("3: 删除学生");
System.out.println("4: 修改学生");
System.out.println("5: 退出");
System.out.println("请输入你的选择:");
// 等待用户输入 ??? 用户不输入, 我们就在这里死等.
// 4. 操作数据(提示, 并接收用户的输入) nextInt
// int choice = sc.nextInt(); // 等待用户输入
String choice = sc.nextLine();
// System.out.println(choice);
// 根据用户的输入, 判断用户的选择
switch (choice) {
case "1":
// 1: 查看所有学生
System.out.println("查看所有学生 ...");
break;
case "2":
// 2: 添加学生
System.out.println("添加学生 ...");
break;
case "3":
// 3: 删除学生
System.out.println("删除学生 ...");
break;
case "4":
// 4: 修改学生
System.out.println("修改学生 ...");
break;
case "5":
// 5. 退出程序
System.out.println("亲, 欢迎您的使用, 下次 `再` 见! ^_^ ");
System.exit(0); // 退出 Java 虚拟机
break;
default:
// 错误操作...
System.out.println("亲, 您输入的有误, 请重新输入!");
break;
}
}
// 3. 关闭资源
// Unreachable code 程序无法抵达的代码
// sc.close();
}
}
2.功能 : 完成添加和查看学生 (单个学生版本)
Student 类的设计代码实现 :
// 1. 类名
public class Student {
// 2. 属性
private String id;
private String name;
private int age;
private String address;
// 构造方法
public Student(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Student() {
}
// 3. 行为
// setter & getter
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
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 void setAddress(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
}
主类代码编写 :
public class StudentManageSystemDemo {
// 属性 : 静态属性不要提供 setter 和 getter 方法.
private static Scanner sc = new Scanner(System.in);
private static Student stu;
// 行为 : 主方法
public static void main(String[] args) {
// 2. 创建键盘录入对象
// Scanner sc = new Scanner(System.in);
// 死循环
while (true) {
System.out.println("----- 欢迎来到学生管理系统 -----");
System.out.println("1: 查看所有学生");
System.out.println("2: 添加学生");
System.out.println("3: 删除学生");
System.out.println("4: 修改学生");
System.out.println("5: 退出");
System.out.println("请输入你的选择:");
// 等待用户输入 ??? 用户不输入, 我们就在这里死等.
// 4. 操作数据(提示, 并接收用户的输入) nextInt
// int choice = sc.nextInt(); // 等待用户输入
String choice = sc.nextLine();
// System.out.println(choice);
// 根据用户的输入, 判断用户的选择
switch (choice) {
case "1":
// 1: 查看所有学生
findAll();
break;
case "2":
// 2: 添加学生
addStudent();
break;
case "3":
// 3: 删除学生
deleteStudent();
break;
case "4":
// 4: 修改学生
updateStudent();
break;
case "5":
// 5. 退出程序
System.out.println("亲, 欢迎您的使用, 下次 `再` 见! ^_^ ");
System.exit(0); // 退出 Java 虚拟机
break;
default:
// 错误操作...
System.out.println("亲, 您输入的有误, 请重新输入!");
break;
}
}
// 3. 关闭资源
// Unreachable code 程序无法抵达的代码
// sc.close();
}
// 定义一个方法, 查看学生
public static void findAll() {
// 判断
if (stu != null) {
System.out.println("学号:\t\t姓名:\t年龄:\t地址:");
// stu -> null 空对象是不可以操作的.
System.out.println(stu.getId() + "\t" + stu.getName() + "\t"+ stu.getAge() +"\t" + stu.getAddress());
} else {
System.out.println("你好, 目前没有任何可查询的学生信息. 请重新选择.");
}
}
// 定义一个方法, 添加学生
public static void addStudent() {
// 1. 提示, 并接收用户输入的数据
System.out.println("亲, 请输入学生的学号 :");
String id = sc.nextLine();
System.out.println("亲, 请输入学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入学生的年龄 :");
// int age = sc.nextInt();
// 方案一 :
// sc.nextLine();
// 方案二 : int age = Integer.parseInt(sc.nextLine()); parse 解析 将一个字符串解析为一个 int 类型的数据
int age = Integer.parseInt(sc.nextLine()); // int -> Integer
System.out.println("亲, 请输入学生的地址 :");
String address = sc.nextLine();
// 2. 将学生相关的数据, 封装到学生类中进行存储
stu = new Student(id, name, age, address);
// 3. 提示添加成功
System.out.println("添加学生信息成功!");
}
// 定义一个方法, 删除学生
public static void deleteStudent() {
System.out.println("删除学生 ...");
}
// 定义一个方法, 修改学生
public static void updateStudent() {
System.out.println("修改学生 ...");
}
}
3.功能 : 完成添加和查看学生 (集合学生版本)
import java.util.Scanner;
public class StudentManageSystemDemo {
// 属性 : 静态属性不要提供 setter 和 getter 方法.
private static Scanner sc = new Scanner(System.in);
// private static Student stu;
// 定义一个集合, 存储学生对象
private static ArrayList<Student> stus = new ArrayList<Student>();
// 行为 : 主方法
public static void main(String[] args) {
// 2. 创建键盘录入对象
// Scanner sc = new Scanner(System.in);
// 死循环
while (true) {
System.out.println("----- 欢迎来到学生管理系统 -----");
System.out.println("1: 查看所有学生");
System.out.println("2: 添加学生");
System.out.println("3: 删除学生");
System.out.println("4: 修改学生");
System.out.println("5: 退出");
System.out.println("请输入你的选择:");
// 等待用户输入 ??? 用户不输入, 我们就在这里死等.
// 4. 操作数据(提示, 并接收用户的输入) nextInt
// int choice = sc.nextInt(); // 等待用户输入
String choice = sc.nextLine();
// System.out.println(choice);
// 根据用户的输入, 判断用户的选择
switch (choice) {
case "1":
// 1: 查看所有学生
findAll();
break;
case "2":
// 2: 添加学生
addStudent();
break;
case "3":
// 3: 删除学生
deleteStudent();
break;
case "4":
// 4: 修改学生
updateStudent();
break;
case "5":
// 5. 退出程序
System.out.println("亲, 欢迎您的使用, 下次 `再` 见! ^_^ ");
System.exit(0); // 退出 Java 虚拟机
break;
default:
// 错误操作...
System.out.println("亲, 您输入的有误, 请重新输入!");
break;
}
}
// 3. 关闭资源
// Unreachable code 程序无法抵达的代码
// sc.close();
}
// 定义一个方法, 查看学生
public static void findAll() {
// 判断
if (stus.size() != 0) {
System.out.println("学号:\t\t姓名:\t年龄:\t地址:");
// 请问 : 如何从集合中取出元素 ???
// 遍历
for (int i = 0; i < stus.size(); i++) {
// 取出
Student stu = stus.get(i);
System.out.println(stu.getId() + "\t" + stu.getName() + "\t"+ stu.getAge() +"\t" + stu.getAddress());
}
} else {
System.out.println("你好, 目前没有任何可查询的学生信息. 请重新选择.");
}
}
// 定义一个方法, 添加学生
public static void addStudent() {
String id = null;
// 标记, 默认 id 就是没有重复的.
boolean flag = false;
// 死循环
while (true) {
// 1. 提示, 并接收用户输入的数据
System.out.println("亲, 请输入学生的学号 :");
id = sc.nextLine(); // itcast_002
// 遍历集合, 查看 id 是否重复
for (int i = 0; i < stus.size(); i++) {
// 取出当前遍历的元素
Student stu = stus.get(i);
// 判断学生的学号是否与用户输入的学号重复了.
if (stu.getId().equals(id)) {
// 条件成立, 说明学号重复了.
flag = true; // flag = true;
break;
}
}
// 请问 : 如何跳出 ??? 没有重复的 id 时, 就跳出.
if (flag == false) {
// 跳出
break;
} else {
System.out.println("亲, 学号不可以重复, 请重新输入 : ");
// 必须在这里将 `标记` 修改为 false
flag = false;
}
}
System.out.println("亲, 请输入学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入学生的年龄 :");
// int age = sc.nextInt();
// 方案一 :
// sc.nextLine();
// 方案二 : int age = Integer.parseInt(sc.nextLine()); parse 解析 将一个字符串解析为一个 int 类型的数据
int age = Integer.parseInt(sc.nextLine()); // int -> Integer
System.out.println("亲, 请输入学生的地址 :");
String address = sc.nextLine();
// 2. 将学生相关的数据, 封装到学生类中进行存储
Student stu = new Student(id, name, age, address);
// 3. 将封装完成数据的学生对象添加 `学生集合中` 存储
// 集合.添加(学生);
stus.add(stu);
// 4. 提示添加成功
System.out.println("添加学生信息成功!");
}
// 定义一个方法, 删除学生
public static void deleteStudent() {
System.out.println("删除学生 ...");
}
// 定义一个方法, 修改学生
public static void updateStudent() {
System.out.println("修改学生 ...");
}
}
4.功能 : 删除学生
// 定义一个方法, 删除学生
public static void deleteStudent() throws IOException {
// 定义一个下标, 从集合中将对应下标的学生对象删除
int index = -1;
// 提示, 并接收用户的输入
System.out.println("亲, 请输入需要删除的学生学号 :");
String id = sc.nextLine();
// 遍历集合
for (int i = 0; i < stus.size(); i++) {
// 取出当前遍历的每一个学生对象
Student stu = stus.get(i);
// 判断学号
if (stu.getId().equals(id)) {
// 学号找到了
index = i;
break;
}
}
// 判断
if (index != -1) {
// 学生信息存储在集合中, 从集合中需要将对应学号的学生信息删除掉
stus.remove(index);
System.out.println("删除成功!");
// 同步一下
writeArrayListToFile();
} else {
System.out.println("您好, 集合中不存在该学号的学生对象, 删除失败!");
}
}
5.功能 : 修改学生
// 定义一个方法, 修改学生
public static void updateStudent() throws IOException {
// 1. 定义一个下标, 用来记录需要修改的学生对象在集合中的下标位置
int index = -1;
// 2. 提示, 并接收用户的输入
System.out.println("亲, 请输入需要修改的学生学号 : ");
String id = sc.nextLine();
// 3. 遍历集合, 判断用户输入的学号在集合中是否存在
for (int i = 0; i < stus.size(); i++) {
// 取出
Student stu = stus.get(i);
// 判断学号
if (stu.getId().equals(id)) {
// 找到了, 记录下标
index = i;
// 跳出
break;
}
}
// 4. 判断
if (index != -1) {
System.out.println("亲, 请输入新学生的姓名 : ");
String name = sc.nextLine();
System.out.println("亲, 请输入新学生的年龄 : ");
int age = Integer.parseInt(sc.nextLine());
System.out.println("亲, 请输入新学生的地址 : ");
String address = sc.nextLine();
// 将用户传入的新数据封装成一个新的学生对象
Student stu = new Student(id, name, age, address);
// 问题 : 修改的就是集合中的学生对象. 集合如何修改 ??? set 方法
stus.set(index, stu);
System.out.println("修改学生对象成功! ");
// 同步一下
writeArrayListToFile();
} else {
System.out.println("您好, 集合中不存在该学号的学生对象, 修改失败!");
}
}
6.IO版学生管理系统 (完整代码)
public class StudentManageSystemDemo {
// 属性 : 静态属性不要提供 setter 和 getter 方法.
private static Scanner sc = new Scanner(System.in);
// private static Student stu;
// 定义一个集合, 存储学生对象
private static ArrayList<Student> stus = new ArrayList<Student>();
// 行为 : 主方法
public static void main(String[] args) throws IOException {
// 程序一旦运行. 立刻从文件中读取数据到集合
readFileToArrayList();
// 2. 创建键盘录入对象
// Scanner sc = new Scanner(System.in);
// 死循环
while (true) {
System.out.println("----- 欢迎来到学生管理系统 -----");
System.out.println("1: 查看所有学生");
System.out.println("2: 添加学生");
System.out.println("3: 删除学生");
System.out.println("4: 修改学生");
System.out.println("5: 退出");
System.out.println("请输入你的选择:");
// 等待用户输入 ??? 用户不输入, 我们就在这里死等.
// 4. 操作数据(提示, 并接收用户的输入) nextInt
// int choice = sc.nextInt(); // 等待用户输入
String choice = sc.nextLine();
// System.out.println(choice);
// 根据用户的输入, 判断用户的选择
switch (choice) {
case "1":
// 1: 查看所有学生
findAll();
break;
case "2":
// 2: 添加学生
addStudent();
break;
case "3":
// 3: 删除学生
deleteStudent();
break;
case "4":
// 4: 修改学生
updateStudent();
break;
case "5":
// 5. 退出程序
System.out.println("亲, 欢迎您的使用, 下次 `再` 见! ^_^ ");
System.exit(0); // 退出 Java 虚拟机
break;
default:
// 错误操作...
System.out.println("亲, 您输入的有误, 请重新输入!");
break;
}
}
// 3. 关闭资源
// Unreachable code 程序无法抵达的代码
// sc.close();
}
// 定义一个方法, 将集合中的数据写入到文件中进行存储
public static void writeArrayListToFile() throws IOException {
// 1. 创建一个 `高效的缓冲区文件写入对象`
BufferedWriter writer = new BufferedWriter(new FileWriter("stus.txt"));
// 2. 黄金三步曲 (写入, 换行, 刷新)
// 2.1 遍历集合
for (int i = 0; i < stus.size(); i++) {
// 2.2 取出当前遍历的学生对象
Student stu = stus.get(i);
// 2.3 查看信息
// System.out.println(stu.getId() + " : " + stu.getName() + " : " +
// stu.getAge() + " : " + stu.getAddress());
// 2.4 创建一个字符串缓冲区对象
StringBuilder sb = new StringBuilder();
// itcast_001,Jack,30,上海
sb.append(stu.getId()).append(",").append(stu.getName()).append(",").append(stu.getAge()).append(",")
.append(stu.getAddress());
// 2.5 黄金三步曲
// 写入
writer.write(sb.toString());
// 换行
writer.newLine();
// 刷新
writer.flush();
}
// 3. 关闭资源
writer.close();
}
// 定义一个方法, 将文件中的数据读取到集合中
public static void readFileToArrayList() throws IOException {
// 1. 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("stus.txt"));
// 2. 一行一行读
String line = null;
while ((line = reader.readLine()) != null) {
// System.out.println(line);
// line -> itcast_001,Jack,30,上海
// 请问 : 字符串切割 split
// split -> {"itcast_001", "Jack", "30", "上海"}
String[] split = line.split(",");
String id = split[0];
String name = split[1];
String age_str = split[2];
int age = Integer.parseInt(age_str);
String address = split[3];
// System.out.println(id + " : " + name + " : " + age + " : " +
// address);
// 将取出的数据封装成每一个学生对象
Student stu = new Student(id, name, age, address);
// 将封装完成数据的学生存储到学生集合中
stus.add(stu);
}
// 3. 关闭资源
reader.close();
}
// 定义一个方法, 查看学生
public static void findAll() {
// 判断
if (stus.size() != 0) {
System.out.println("学号:\t\t姓名:\t年龄:\t地址:");
// 请问 : 如何从集合中取出元素 ???
// 遍历
for (int i = 0; i < stus.size(); i++) {
// 取出
Student stu = stus.get(i);
System.out.println(stu.getId() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t" + stu.getAddress());
}
} else {
System.out.println("你好, 目前没有任何可查询的学生信息. 请重新选择.");
}
}
// 定义一个方法, 添加学生
public static void addStudent() throws IOException {
String id = null;
// 标记, 默认 id 就是没有重复的.
boolean flag = false;
// 死循环
while (true) {
// 1. 提示, 并接收用户输入的数据
System.out.println("亲, 请输入学生的学号 :");
id = sc.nextLine(); // itcast_002
// 遍历集合, 查看 id 是否重复
for (int i = 0; i < stus.size(); i++) {
// 取出当前遍历的元素
Student stu = stus.get(i);
// 判断学生的学号是否与用户输入的学号重复了.
if (stu.getId().equals(id)) {
// 条件成立, 说明学号重复了.
flag = true; // flag = true;
break;
}
}
// 请问 : 如何跳出 ??? 没有重复的 id 时, 就跳出.
if (flag == false) {
// 跳出
break;
} else {
System.out.println("亲, 学号不可以重复, 请重新输入 : ");
// 必须在这里将 `标记` 修改为 false
flag = false;
}
}
System.out.println("亲, 请输入学生的姓名 :");
String name = sc.nextLine();
System.out.println("亲, 请输入学生的年龄 :");
// int age = sc.nextInt();
// 方案一 :
// sc.nextLine();
// 方案二 : int age = Integer.parseInt(sc.nextLine()); parse 解析 将一个字符串解析为一个
// int 类型的数据
int age = Integer.parseInt(sc.nextLine()); // int -> Integer
System.out.println("亲, 请输入学生的地址 :");
String address = sc.nextLine();
// 2. 将学生相关的数据, 封装到学生类中进行存储
Student stu = new Student(id, name, age, address);
// 3. 将封装完成数据的学生对象添加 `学生集合中` 存储
// 集合.添加(学生);
stus.add(stu);
// 4. 提示添加成功
System.out.println("添加学生信息成功!");
// 5. 将文件和集合进行同步
writeArrayListToFile();
}
// 定义一个方法, 删除学生
public static void deleteStudent() throws IOException {
// 定义一个下标, 从集合中将对应下标的学生对象删除
int index = -1;
// 提示, 并接收用户的输入
System.out.println("亲, 请输入需要删除的学生学号 :");
String id = sc.nextLine();
// 遍历集合
for (int i = 0; i < stus.size(); i++) {
// 取出当前遍历的每一个学生对象
Student stu = stus.get(i);
// 判断学号
if (stu.getId().equals(id)) {
// 学号找到了
index = i;
break;
}
}
// 判断
if (index != -1) {
// 学生信息存储在集合中, 从集合中需要将对应学号的学生信息删除掉
stus.remove(index);
System.out.println("删除成功!");
// 同步一下
writeArrayListToFile();
} else {
System.out.println("您好, 集合中不存在该学号的学生对象, 删除失败!");
}
}
// 定义一个方法, 修改学生
public static void updateStudent() throws IOException {
// 1. 定义一个下标, 用来记录需要修改的学生对象在集合中的下标位置
int index = -1;
// 2. 提示, 并接收用户的输入
System.out.println("亲, 请输入需要修改的学生学号 : ");
String id = sc.nextLine();
// 3. 遍历集合, 判断用户输入的学号在集合中是否存在
for (int i = 0; i < stus.size(); i++) {
// 取出
Student stu = stus.get(i);
// 判断学号
if (stu.getId().equals(id)) {
// 找到了, 记录下标
index = i;
// 跳出
break;
}
}
// 4. 判断
if (index != -1) {
System.out.println("亲, 请输入新学生的姓名 : ");
String name = sc.nextLine();
System.out.println("亲, 请输入新学生的年龄 : ");
int age = Integer.parseInt(sc.nextLine());
System.out.println("亲, 请输入新学生的地址 : ");
String address = sc.nextLine();
// 将用户传入的新数据封装成一个新的学生对象
Student stu = new Student(id, name, age, address);
// 问题 : 修改的就是集合中的学生对象. 集合如何修改 ??? set 方法
stus.set(index, stu);
System.out.println("修改学生对象成功! ");
// 同步一下
writeArrayListToFile();
} else {
System.out.println("您好, 集合中不存在该学号的学生对象, 修改失败!");
}
}
}
————就业班————
1.面向对象 – 继承
1.1 继承的引入
案例 : 写一个学生类, 教师类, 校长类.
1.1.1 普通方式实现 :
知识点概述 :
1.私有化成员属性.
2.提供相应的 setter & getter 方法.
3.构造方法.
// 定义一个 Student 类
public class Student {
// 属性
// 私有化 : 对内开放, 对外保护.
// 对外提供 公共的访问方法
. setter & getter
private String name; // 姓名
private int age; // 年龄
private char gender; // 性别 ‘男’ \ ‘女’ ‘M’ Male \ ‘F’ Female
private String stuNumber; // 学号
// 行为
public void introduce() {
System.out.println("大家好, 我叫 " + name + ", 我今年 " + age + " 岁了. " + gender);
System.out.println("我的学号是: " + stuNumber);
}
// 构造方法 : 在创建创建的同时为对象的属性进行初始化
public Student(String name, int age, char gender, String stuNumber) {
this.name = name;
this.age = age;
this.gender = gender;
this.stuNumber = stuNumber;
}
// 注意 : 构造方法一旦重载, 系统就不再提供默认的无参构造方法.
// 解决方案 : 手动提供
public Student() {
}
// setter & getter
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 void setGender(char gender) {
this.gender = gender;
}
public char getGender() {
return gender; // this.gender;
}
public void setStuNumber(String stuNumber) {
this.stuNumber = stuNumber;
}
public String getStuNumber() {
return stuNumber;
}
}
// 自定义一个 Teacher 类
public class Teacher {
// 属性
private String name;
private int age;
private char gender;
private String teachingField; // 教学领域
// 行为
public void introduce() {
System.out.println("大家好, 我叫 " + name + ", 我今年 " + age + " 岁了. " + gender);
System.out.println("我的教学领域是: " + teachingField);
}
// 构造方法 : 在创建创建的同时为对象的属性进行初始化
public Teacher(String name, int age, char gender, String teachingField) {
this.name = name;
this.age = age;
this.gender = gender;
this.teachingField = teachingField;
}
// 注意 : 构造方法一旦重载, 系统就不再提供默认的无参构造方法.
// 解决方案 : 手动提供
public Teacher() {
}
// setter & getter
public String getName() {
return name;
}
public int getAge() {
return age;
}
public char getGender() {
return gender;
}
public String getTeachingField() {
return teachingField;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setGender(char gender) {
this.gender = gender;
}
public void setTeachingField(String teachingField) {
this.teachingField = teachingField;
}
}
// 定义一个 SchoolMaster 类
public class SchoolMaster {
// 属性
private String name;
private int age;
private char gender;
private int workingYears; // 工作年限
// 行为
public void introduce() {
System.out.println("大家好, 我叫 " + name + ", 我今年 " + age + " 岁了. " + gender);
System.out.println("我的工作年限是: " + workingYears);
}
// 构造方法 : 在创建创建的同时为对象的属性进行初始化
public SchoolMaster(String name, int age, char gender, int workingYears) {
this.name = name;
this.age = age;
this.gender = gender;
this.workingYears = workingYears;
}
// 注意 : 构造方法一旦重载, 系统就不再提供默认的无参构造方法.
// 解决方案 : 手动提供
public SchoolMaster() {
}
// setter & getter
public String getName() {
return name;
}
public int getAge() {
return age;
}
public char getGender() {
return gender;
}
public int getWorkingYears() {
return workingYears;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setGender(char gender) {
this.gender = gender;
}
public void setWorkingYears(int workingYears) {
this.workingYears = workingYears;
}
}
测试类 :
public class ExtendsDemo {
public static void main(String[] args) {
/*
// 需求 : 根据 Student 类创建一个对象
Student stu = new Student();
System.out.println(stu);
// 赋值
stu.setName("学生");
stu.setAge(18);
stu.setGender('女');
stu.setStuNumber("itcast_007");
// 取值
System.out.println(stu.getName());
System.out.println(stu.getAge());
System.out.println(stu.getGender());
System.out.println(stu.getStuNumber());
*/
// 创建对象的同时为对象的属性赋值
Student stu2 = new Student("学生", 18, '女', "itcast_001");
stu2.introduce();
Teacher t = new Teacher("老师", 30, '男', "english, sport");
t.introduce();
SchoolMaster master = new SchoolMaster("校长", 50, '女', 30);
master.introduce();
}
}
问题 :
1.重复. (不同类的代码重复)
2.类是独立存在的. 如何抽取 ??? 不能使用 函数 / 方法
抽取了.
1.1.2 继承方式实现 :
继承是 类在继承, 不是对象在对象. 在我们设计类的同时, 需要思考类之间的继承关系.
继承解决什么问题 : 继承解决了不同类中重复代码的问题.
继承的核心 : 子类无条件拥有父类中所有可继承的属性和方法.
// 定义一个 Person 类. 爸爸
-> 父类
-> 共性类
public class Person {
// 属性
// 私有化 : 对内开放, 对外保护.
// 对外提供 公共的访问方法
. setter & getter
private String name; // 姓名
private int age; // 年龄
private char gender; // 性别 ‘男’ \ ‘女’ ‘M’ Male \ ‘F’ Female
// 行为
public void introduce() {
System.out.println("大家好, 我叫 " + name + ", 我今年 " + age + " 岁了. " + gender);
}
// setter & getter
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 void setGender(char gender) {
this.gender = gender;
}
public char getGender() {
return gender; // this.gender;
}
}
// 定义一个 Student 类, 继承 Person 类
// 继承的特点 : 子类 无条件
拥有父类中所有可继承的属性和行为. 构造方法无法继承.
public class Student extends Person {
// 属性
private String stuNumber; // 学号
public void setStuNumber(String stuNumber) {
this.stuNumber = stuNumber;
}
public String getStuNumber() {
return stuNumber;
}
}
// 自定义一个 Teacher 类, 继承 person 类
public class Teacher extends Person {
// 属性
private String teachingField; // 教学领域
// setter & getter
public String getTeachingField() {
return teachingField;
}
public void setTeachingField(String teachingField) {
this.teachingField = teachingField;
}
}
// 定义一个 SchoolMaster 类, 继承 Person 类
public class SchoolMaster extends Person {
// 属性
private int workingYears; // 工作年限
// setter & getter
public int getWorkingYears() {
return workingYears;
}
public void setWorkingYears(int workingYears) {
this.workingYears = workingYears;
}
}
测试类 :
public class ExtendsDemo {
public static void main(String[] args) {
// 需求 : 根据 Student 类创建一个对象
// new Student() 在堆区中创建一个 Student 类型的对象. 确定: 堆区中到底有几个对象 ??? 一个
Student stu = new Student();
System.out.println(stu);
// 赋值
stu.setName("学生");
stu.setAge(18);
stu.setGender('女');
stu.setStuNumber("itcast_007");
// 取值
System.out.println(stu.getName());
System.out.println(stu.getAge());
System.out.println(stu.getGender());
System.out.println(stu.getStuNumber());
}
}
1.2 继承的练习
案例 : 继承的代码实现 (掌握)
说明 : Animal 是父类, Dog 和 Cat 都是 Animal 的子类. ArmyDog(军犬) 是 Dog 的子类.
1.3 面向对象-方法重写 Override
代码案例 : 有一个 “人” 类.拥有一个sayHi的方法. 又有三个子类,”中国人”, “韩国人”, “日本人”. 三个子类都各自重写了sayHi方法.因为三个子类对同一种行为都有其不同的表现形式.
实现方案一 :
// 定义一个父类
public class Person {
// 属性
// 行为
public void sayHi() {
System.out.println("大家好, 我是人.");
}
}
// 定义一个 Chinese 类, 继承 Person 类
public class Chinese extends Person {
// 属性
// 行为
}
public class Korean extends Person {
// 属性
// 行为
}
public class Japanese extends Person {
// 属性
// 行为
}
测试类 :
public class OverrideDemo {
public static void main(String[] args) {
// 1. 创建一个伟大的 Chinese 对象
Chinese c = new Chinese();
c.sayHi();
// 2. 创建一个妩媚的 Korean 对象
Korean k = new Korean();
k.sayHi();
// 3. 创建一个猥琐的 Japanese 对象
Japanese j = new Japanese();
j.sayHi();
}
}
问题 : 与现实生活的情景不相同. 因为, 每一个国家的人都有自己特有的打招呼方式.
作用 : 子类继承父类, 可以继承父类中的所有可继承的属性和行为, 此时, 创建子类对象, 调用方法, 可以直接调用父类中继承而来的方法.
如果子类该实现与父类不同, 如何修改 ???
实现方案二 : 方法的重写 :
// 定义一个父类
public class Person {
// 属性
// 行为
public void sayHi() {
System.out.println("大家好, 我是人.");
}
}
public class Korean extends Person {
// 属性
// 行为
// 需求 : 韩国人希望有自己特有的打招呼方式.
// 解决方案 : 方法的重写. override
/*
* 重写的前提条件 :
* 1. 方法名必须相同.
* 2. 参数列表必须相同.
* 3. 返回值类型必须相同.
*
* 注意 :
* 方法的修饰权限必须要大于等于父类的权限. public > protected > 默认 > private
*/
@Override
public void sayHi() {
System.out.println("阿尼哈斯哟, 泡菜思密达...");
}
}
public class Japanese extends Person {
// 属性
// 行为
@Override
public void sayHi() {
System.out.println("亚麻得, 八嘎!");
}
}
// 定义一个 Chinese 类, 继承 Person 类
public class Chinese extends Person {
// 属性
// 行为
// alt + shift + s
@Override // 注解 : 检查该方法是否是父类中重写的方法, 如果是, 编译通过, 如果不是, 编译失败!
public void sayHi() {
System.out.println("你吃了吗?");
}
}
测试二 :
public class OverrideDemo {
public static void main(String[] args) {
// 1. 创建一个伟大的 Chinese 对象
Chinese c = new Chinese();
c.sayHi();
// 2. 创建一个妩媚的 Korean 对象
Korean k = new Korean();
k.sayHi();
// 3. 创建一个猥琐的 Japanese 对象
Japanese j = new Japanese();
j.sayHi();
}
}
结果说明了如果子类重写了父类的方法, 程序执行时, 调用子类重写的该方法, 不会在调用父类中继承而来的方法.
如果子类没有重写父类的方法, 程序执行时, 再调用父类中继承而来的方法. 这就是方法的调用顺序.
1.4. super 关键字 (子父类中构造方法的特点)
/*
* 构造方法中, 关于 super 关键字的使用.
*/
public class ConstructorSuperDemo {
public static void main(String[] args) {
// Son(); 调用 Son 类的无参构造方法
Son son = new Son("儿子", 18, '男', "itcast_007");
}
}
// 定义一个类 : 一个 .java 源文件中只能定义一个 public 类.
/*class GrandPa {
// 构造方法
public GrandPa() {
super();
System.out.println(“GrandPa 无参构造方法被调用…”);
}
}
*/
/*
* 属性初始化的要诀 :
* 1. 父类的属性父类自己初始化.
* 2. 子类的属性子类自己初始化.
*/
class Father {
// 属性
private String name;
private int age;
private char gender;
// 构造方法 :
// 问题 : 父类为什么要提供一个 `无参的构造方法` 呢 ?
// 说明 : 为了方便子类对象的创建.
public Father() {
super();
System.out.println("Father 无参构造方法被调用...");
}
public Father(String name, int age, char gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
System.out.println("Father 带参构造方法被调用...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
class Son extends Father {
// 属性
private String id;
// 构造方法
public Son() {
// 构造方法中的第一句代码就是调用父类的无参构造方法. (默认的)
super();
System.out.println("Son 无参构造方法被调用...");
}
public Son(String name, int age, char gender, String id) {
// 必须先有父类, 父类的属性会先进行初始化, 然后回来再初始化子类的属性.
super(name, age, gender);
this.id = id;
// Constructor call must be the first statement in a constructor
// 构造方法的调用必须位于构造方法的第一条语句
// super(name, age, gender);
System.out.println("Son 带参构造方法被调用...");
// 父类的名称儿子类给.
// super.setName(name);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
1.5 综合练习 :
案例 : 写一个学生类, 教师类, 校长类.
属性 : 姓名, 年龄, 性别. Student (学号), Teacher (教学领域), SchoolMaster (工作年限)
行为 : 介绍自己.
Person 类 :
// 定义一个 Person 类. 爸爸
-> 父类
-> 共性类
public class Person {
// 属性
// 私有化 : 对内开放, 对外保护.
// 对外提供 公共的访问方法
. setter & getter
private String name; // 姓名
private int age; // 年龄
private char gender; // 性别 ‘男’ \ ‘女’ ‘M’ Male \ ‘F’ Female
// 行为
public void introduce() {
System.out.println("大家好, 我叫 " + name + ", 我今年 " + age + " 岁了. " + gender);
}
// 构造方法重载
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 父类必须要手动提供一个无参的构造方法.
// 作用 : 方便子类创建对象.
public Person() {
}
// setter & getter
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 void setGender(char gender) {
this.gender = gender;
}
public char getGender() {
return gender; // this.gender;
}
}
Student 类 :
// 定义一个 Student 类, 继承 Person 类
// 继承的特点 : 子类 无条件
拥有父类中所有可继承的属性和行为. 构造方法无法继承.
public class Student extends Person {
// 属性
private String stuNumber; // 学号
// 重写父类中定义的 introduce 方法
@Override // 作用 : 检查该方法是否为父类中重写的方法, 如果是, 编译通过, 否则, 编译失败!
public void introduce() {
// 调用父类中该方法的代码执行逻辑
super.introduce();
System.out.println("我的学号是: " + stuNumber);
}
// 构造方法重载
public Student(String name, int age, char gender, String stuNumber) {
// 调用父类的构造方法, 将 name, age, gender 传递给父类进行初始化
super(name, age, gender);
// 自己的属性自己赋值
this.stuNumber = stuNumber;
}
// 手动提供一个无参的构造方法
public Student() {
}
public void setStuNumber(String stuNumber) {
this.stuNumber = stuNumber;
}
public String getStuNumber() {
return stuNumber;
}
}
测试代码 :
public class ExtendsDemo {
public static void main(String[] args) {
// 需求 : 根据 Student 类创建一个对象
// new Student() 在堆区中创建一个 Student 类型的对象. 确定: 堆区中到底有几个对象 ??? 一个
// 赋值
Student stu = new Student("学生", 18, '女', "itcast_007");
// 取值
stu.introduce();
System.out.println("---------------");
Student stu2 = new Student();
stu2.introduce();
}
}
// 自定义一个 Teacher 类, 继承 person 类
public class Teacher extends Person {
// 属性
private String teachingField; // 教学领域
// 构造方法重载
public Teacher(String name, int age, char gender, String teachingField) {
super(name, age, gender);
this.teachingField = teachingField;
}
public Teacher() {
}
// 重载父类中继承而来的 introduce 方法
@Override
public void introduce() {
super.introduce();
System.out.println("我的教学领域是 : " + teachingField);
}
// setter & getter
public String getTeachingField() {
return teachingField;
}
public void setTeachingField(String teachingField) {
this.teachingField = teachingField;
}
}
// 定义一个 SchoolMaster 类, 继承 Person 类
public class SchoolMaster extends Person {
// 属性
private int workingYears; // 工作年限
// 构造方法重载
public SchoolMaster(String name, int age, char gender, int workingYears) {
super(name, age, gender);
this.workingYears = workingYears;
}
public SchoolMaster() {
}
// 重写 introduce() 方法
@Override
public void introduce() {
super.introduce();
System.out.println("我的工作年限是 : " + workingYears);
}
// setter & getter
public int getWorkingYears() {
return workingYears;
}
public void setWorkingYears(int workingYears) {
this.workingYears = workingYears;
}
}
测试代码 :
public class ExtendsDemo {
public static void main(String[] args) {
// 需求 : 根据 Student 类创建一个对象
// new Student() 在堆区中创建一个 Student 类型的对象. 确定: 堆区中到底有几个对象 ??? 一个
// 赋值
Student stu = new Student("学生", 18, '女', "itcast_007");
// 取值
stu.introduce();
System.out.println("---------------");
Student stu2 = new Student();
stu2.introduce();
System.out.println("---------------");
// Teacher 类
Teacher t = new Teacher("老师", 30, '男', "english, sport, java");
t.introduce();
System.out.println("---------------");
// SchoolMaster
// 自动声明 `局部变量` ctrl + 2 -> 选择 L 键
SchoolMaster master = new SchoolMaster("校长", 50, '女', 30);
master.introduce();
}
}
程序的理解 : 父类的引用指向了子类的对象.这就是多态.
生活的理解 : 对于同一种行为(cut), 根据传入的不同对象, 产生不同的执行结果.
/*
* 设计三个类 :
*
* 设计一个 Person 类型, name, doSomething() 方法
*
* 1. Barber 理发师
* 2. Actress 女演员
* 3. Doctor 医生
*
* 多态 : 同一个行为, 对于传入不同的对象而言, 具有完全不同的表现形式.
*
* 需求 : 定义一个 cut() 行为, 传入不同的对象, 查看不同的执行结果.
*/
2.1 多态的第一种使用场景 (方法参数设计)
多态的前提条件是 继承
.
Person 类设计 :
public class Person {
// 属性
private String name;
// 行为
public void doSomething() {
System.out.println(name + " 正在做XXXOOO...");
}
// setter & getter
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Barber extends Person {
// 属性
// 行为
// 重写父类中的 doSomething 方法
// 1. 写重写的方法名 2. alt + / 选择 override
@Override
public void doSomething() {
// 调用父类中的代码逻辑, 如果不需要, 不写.
// super.doSomething();
System.out.println(getName() + " 正在认真细致的给顾客剪头发.");
}
}
public class Actress extends Person {
// 属性
// 行为
@Override
public void doSomething() {
System.out.println(getName() + " 正在准备停止表演...");
}
}
public class Doctor extends Person {
// 属性
// 行为
@Override
public void doSomething() {
System.out.println(getName() + " 正在准备给你开膛破肚...");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// 1. Barber
Barber b = new Barber();
b.setName(“理发师”);
b.doSomething();
// 2. Actress
Actress a = new Actress();
a.setName("女演员");
a.doSomething();
// 3. Doctor
Doctor d = new Doctor();
d.setName("女医生");
d.doSomething();
}
}
没有重写之前的执行结果 :
重写之后的执行结果 :
public class PolymorphismDemo {
public static void main(String[] args) {
// 1. Barber
Barber b = new Barber();
b.setName(“理发师”);
// 2. Actress
Actress a = new Actress();
a.setName("女演员");
// 3. Doctor
Doctor d = new Doctor();
d.setName("女医生");
// 调用 cut 行为
cut(d);
}
// 定义一个行为 : cut
// not applicable for the arguments 参数不兼容, 类型不匹配.
// 方法的重载 : 1. 方法名必须相同. 2. 参数列表的类型或长度必须不同 3. 与返回值类型和参数名称无关.
public static void cut(Barber b) {
b.doSomething();
}
public static void cut(Actress a) {
a.doSomething();
}
public static void cut(Doctor d) {
d.doSomething();
}
}
代码存在的问题 : cut 要根据不同传入的不同对象设计不同的方法重载, 方法特别多.
需求 : 如何设计一个通用的 cut 方法 ???
解决方案 : 使用 Java 多态语句解决方法 cut 的设计参数.
public class PolymorphismDemo {
public static void main(String[] args) {
// 1. Barber
Barber b = new Barber();
b.setName(“理发师”);
// 2. Actress
Actress a = new Actress();
a.setName("女演员");
// 3. Doctor
Doctor d = new Doctor();
d.setName("女医生");
// 调用 cut 行为
cut(b);
}
// 定义一个行为 : cut
// 理发师, 女演员, 女医生 统称是什么 ? 是 Person
// 多态方法的设计 : 该方法可以对于传入不同的对象, 执行不同的行为.
// 原因 : cut 方法需要什么 ? 需要人. 传入的是什么 ? 理发师, 女演员, 女医生 他们是不是人??? 是.
// 总结 : 当在程序中设计方法时, 尽量将方法的参数设计为 `父类类型`, 因为Java的多态中, 父类类型可以接收所有的子类对象, 因为 `子类就是父类`.
public static void cut(Person p) {
// 继承的 is a 关系. Barber is a Person. Actress is a Perosn, Doctor is a Person.
p.doSomething();
}
}
2.2 多态的第二种使用场景 (对象引用接收)
测试 : 使用具体子类类型接收对象产生的问题演示 :
public class PolymorphismDemo {
public static void main(String[] args) {
// 问题 : 一旦需要对象, 就会产生大量的代码修改.
Doctor d = new Doctor();
d.setName(“女医生”);
// 调用 cut 行为
cut(d);
}
// 定义一个行为 : cut
// 理发师, 女演员, 女医生 统称是什么 ? 是 Person
// 多态方法的设计 : 该方法可以对于传入不同的对象, 执行不同的行为.
// 原因 : cut 方法需要什么 ? 需要人. 传入的是什么 ? 理发师, 女演员, 女医生 他们是不是人??? 是.
// 总结 : 当在程序中设计方法时, 尽量将方法的参数设计为 `父类类型`, 因为Java的多态中, 父类类型可以接收所有的子类对象, 因为 `子类就是父类`.
public static void cut(Person p) {
// 继承的 is a 关系. Barber is a Person. Actress is a Perosn, Doctor is a Person.
p.doSomething();
}
}
问题 : 一旦对象发生改变, 我们程序就需要进行大量的代码修改.
需求 : 有没有一种方式可以直接去改变对象, 而不造成大量的代码修改.
解决方案 : 使用父类的引用指向子类的对象.
public class PolymorphismDemo {
public static void main(String[] args) {
// Barber is a Person;
// 多态的第二种使用场景 : 创建子类对象, 使用父类的引用进行接收. 简化更改对象后代码的修正.
// 多态 : 父类的引用指向子类的对象. 这就是多态.
Person p = new Doctor();
p.setName("女医生");
// 调用 cut 行为
cut(p);
}
// 定义一个行为 : cut
// 理发师, 女演员, 女医生 统称是什么 ? 是 Person
// 多态方法的设计 : 该方法可以对于传入不同的对象, 执行不同的行为.
// 原因 : cut 方法需要什么 ? 需要人. 传入的是什么 ? 理发师, 女演员, 女医生 他们是不是人??? 是.
// 总结 : 当在程序中设计方法时, 尽量将方法的参数设计为 `父类类型`, 因为Java的多态中, 父类类型可以接收所有的子类对象, 因为 `子类就是父类`.
public static void cut(Person p) {
// Person p = new Doctor(); 父类的引用指向了子类的对象.
// 继承的 is a 关系. Barber is a Person. Actress is a Perosn, Doctor is a Person.
p.doSomething();
}
}
2.3 多态的弊端
说明 : 父类引用无法直接调用子类对象的特有方法.如果要调用子类特有方法, 必须要向下转换, 向下转型有风险, 转型之前必须要进行类型的判断. 使用 instanceof 关键字.
步骤一 : 给三个子类对象增加其 特有的行为
.
1.理发师 : chasingGirls(); 追女孩.
2.女演员 : beFamous(); 成名.
3.女医生 : temptationOfUniforms(); 制服诱惑.
测试一 : 使用具体的子类对象接收.
public class PolymorphismDemo {
public static void main(String[] args) {
// 正常测试 :
// 1. Barber
Barber b = new Barber();
b.setName("理发师");
b.doSomething();
b.chasingGirls();
// 2. Actress
Actress a = new Actress();
a.setName("女演员");
a.doSomething();
a.beFamous();
// 3. Doctor
Doctor d = new Doctor();
d.setName("女医生");
d.doSomething();
d.temptationOfUniforms();
}
public static void cut(Person p) {
p.doSomething();
}
}
测试二 : 使用父类类型接收子类对象, 并实现在 cut 方法中执行了 子类特有的方法
方案 : 在cut方法中, 需要对传入的参数对象进行向下转换.
向下转型的目的 : 为了调用子类特有的方法.
问题方法的演示 : 不推荐大量练习, 明白问题即可.
public class PolymorphismDemo {
public static void main(String[] args) {
// 正常测试 :
/*
// 1. Barber
Barber b = new Barber();
b.setName(“理发师”);
b.doSomething();
b.chasingGirls();
// 2. Actress
Actress a = new Actress();
a.setName("女演员");
a.doSomething();
a.beFamous();
*/
// 请问 : 编译器认为 num 是什么类型的 ? float
// 具体对象是什么类型, 取决于 `左边真正的接收类型`.
// float num = 10;
// System.out.println(num); // 10.0
// 多态 : 使用父类引用接收子类对象
Person p = new Doctor();
p.setName("女医生");
cut(p);
}
public static void cut(Person p) {
// 此句代码为什么可以不用向下转型 ???
// 方法重写 : 程序执行时, 如果子类重写了父类的方法, 程序就会执行子类重写的方法.
p.doSomething();
// The method temptationOfUniforms() is undefined for the type Person
// 在Person类中没有定义 temptationOfUniforms() 方法.
// 编译原理 : 编译器认为 p 对象是 Person 类型的, 因此编译器会去 Person 类中寻找 temptationOfUniforms() 方法.
// 需求 : 在内部就要执行 temptationOfUniforms(); 该方法, 如何实现 ??? 向下转型.
// 含义: 告诉编译器 p 不是Person类型的, 而是 Doctor类型的.
// 向下转型的目的 : 为了调用子类的特有方法.
Doctor d = (Doctor) p;
d.temptationOfUniforms();
}
}
说明 : 在多态方法设计中调用子类特有方法的解决思路, 使用 instanceof 关键字在向下转型之前对传入的参数对象进行类型的判断.
正确的解决方案 :
public class PolymorphismDemo {
public static void main(String[] args) {
// 多态 : 使用父类引用接收子类对象
Person p = new Doctor();
p.setName("女医生");
cut(p);
}
// 如果该方法编译不错误, 运行错误, 说明方法设计有问题!
public static void cut(Person p) {
p.doSomething();
// 向下转型的目的 : 为了调用子类的特有方法.
// ClassCastException : 类型转换异常 Actress cannot be cast to Doctor 女演员类型的对象不能被转换为医生类型的对象.
// 异常一旦发生, 程序会终止了, 也就是说, 无法继续执行后续代码了.
// 向下转换有风险, 实现需谨慎!
// 解决方案 : 对象 instanceof 类型 含义: 判断左边的对象是否为右边的类型, 如果是, 返回 true, 否则, false
if (p instanceof Barber) {
Barber b = (Barber) p;
b.chasingGirls();
} else if (p instanceof Actress) {
Actress a = (Actress) p;
a.beFamous();
} else if (p instanceof Doctor) {
Doctor d = (Doctor) p;
d.temptationOfUniforms();
}
}
}
3.final 关键字
中国(China)、俄罗斯(Russia)、巴西(Brazil)、 印度(India)、 南非(South Africa)
/*
* final 小结 :
* 1. final 修饰的方法, 子类可以继承, 但是不能篡改. (不常用)
* 2. final 修改类, 表示该类不可以被继承. (不常用, 系统哪些类不可以被继承)
* 3. final 修饰成员变量, 意味着该成员变量即成为了 常量
. 不能再次更改. (注意: 成员变量有默认初始化, 不可以先定义, 后赋值) (常用)
* 4. final 修饰局部变量, 意味着该局部变量不能重新赋值, 但可以先定义, 后赋值, 因为局部变量没有默认初始值. (不常用)
*
* 成员变量 : 定义类中, 方法外, 可以在整个类所访问.
* 局部变量 : 定义在方法中, 只能被该方法所使用.
*/
public class AnimalDeclarationDemo {
public static void main(String[] args) {
/*// 1. 创建一个子类对象
Animal a = new Fox();
double PI = Fox.PI;
System.out.println(PI);
// Fox.PI = 2.14;
a.decleration();*/
Fox fox = new Fox();
fox.test();
}
}
// 记住 : 被 final 修饰的类, 不能被继承, 只能老老实实学习别人定义的该类.
public /final/ class Animal {
// 属性
// 行为 : 联合声明
// 需求 : 该联合声明子类可以使用, 但是不允许子类进行篡改.
// 如何实现 : 在方法之前添加关键字 : final `最终的, 无法更改的` 的含义.
public final void decleration() {
System.out.println("动物们的联合声明: 我们动物是人类的好朋友.");
}
}
// The type Dog cannot subclass the final class Animal
// Dog 类型不能是 Animal 类的子类 (被 final 修饰的类不能被继承)
public class Dog extends Animal {
}
public class Fox extends Animal {
// 属性
// The blank final field age may not have been initialized 空白的 final 属性 age 可能没有被初始化.
// final 修饰的属性没有默认初始化. 必须手动初始化.
// public final int age = 18;
public static final double PI = 3.14; // url, username, password
// 行为
// Cannot override the final method from Animal
// 不能重写从 Animal 类中指定的 final 修饰的方法
/*@Override
public void decleration() {
System.out.println("动物们的联合声明: 我们动物是人类的天敌.");
}*/
// 测试方法
public void test() {
// 修改属性的数值
// The final field Fox.age cannot be assigned final 修饰的属性 age 不能重新分配数值.
// age = 30;
// PI = 2.14;
// 局部变量 : 没有默认初始值. 所以可以先定义, 后赋值.
final int num = 10;
// The final local variable 局部变量 num cannot be assigned 不能被赋值.
// It must be blank and not using a compound assignment
// 该变量要么为空值. 要么合在一起书写并赋值.
// num = 998;
System.out.println(num);
}
}
4.关于静态属性的说明
public class Student {
// 属性 :
// 静态属性节省内存空间, 在内存中仅有一份. 共享, 不允许修改
// public static final 基本在程序方法中, 此三个修饰会同时出现.
public static final String schoolName = “上海传智播客”;
// π
public static final double PI = 3.14;
// 行为
}
public class StudentDemo {
public static void main(String[] args) {
// The static field Student.schoolName should be accessed in a static way
// 指定的静态属性应该使用静态的方式进行访问
// Student.schoolName = "北京传智播客";
Student stu1 = new Student();
System.out.println(stu1.schoolName);
Student stu2 = new Student();
System.out.println(stu2.schoolName);
Student stu3 = new Student();
System.out.println(stu3.schoolName);
}
}
5.抽象类
4.1 抽象类的引入
/*
* 抽象类的使用注意事项 :
* 1. 没有方法体的方法必须被定义为 抽象方法
.
* 2. 拥有抽象方法的类必须被定义为 抽象类
.
* 3. 抽象类不能实例化对象.
*/
4.2 抽象类的使用
// 抽象类的作用 : 定义子类中共有的 行为规范
.
public abstract class Animal {
// 属性
// 行为
// 1. 没有方法体的方法必须定义为 `抽象方法`
public abstract void shout();
public abstract void eat();
}
// 子类如果继承了抽象类, 必须要重写父类中的抽象方法.
public class Dog extends Animal {
@Override
public void shout() {
System.out.println("汪汪汪...");
}
@Override
public void eat() {
System.out.println("跟我混, 我骨头吃...");
}
}
public class Cat extends Animal {
// 传智播客 : 科技喵喵喵
@Override
public void shout() {
System.out.println("喵喵喵...");
}
@Override
public void eat() {
System.out.println("跟我混, 有鱼翅吃...");
}
}
public class Wolf extends Animal {
@Override
public void shout() {
System.out.println("嗷嗷嗷...");
}
@Override
public void eat() {
System.out.println("跟我混, 有肉吃...");
}
}
测试类 :
public class AbstractClassDemo {
public static void main(String[] args) {
// 需求 : 创建一个Animal 类型的对象
// Cannot instantiate 实例化 the type Animal
// 3. 抽象类不能实例化对象, 也就是说, 抽象类不能使用 new 关键字.
// Animal a = new Animal();
// 实例化抽象类的子类对象
// Dog, Cat, Wolf
// 抽象类的使用方式 : 看父类, 创建子类, 使用父类中的定义的行为.
// Reader reader = new FileReader("路径"); reader.read(); read 方法是父类中定义的方法.
Animal a = new Wolf();
a.shout();
a.eat();
}
}
4.3 抽象类中的非抽象方法
// 抽象类的作用 : 定义子类中共有的 行为规范
.
// 抽象类中不仅可以定义抽象方法, 同时也可以定义 非抽象方法
.
public abstract class Animal {
// 属性
private String name;
// 行为
// 1. 没有方法体的方法必须定义为 `抽象方法`
public abstract void shout();
public abstract void eat();
// 增加一个非抽象方法
public void run() {
System.out.println(name + " 正在疯狂的裸奔..."); // 成功学 -> 绝不裸奔
}
// setter & getter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class AbstractClassDemo {
public static void main(String[] args) {
Animal a = new Wolf();
a.shout();
a.eat();
a.setName("灰太狼");
a.run();
}
}
6.接口
5.1 接口在程序中的使用
定义两个接口 :
// 定义了一个 缉毒
接口
public interface DrugDetectable {
// 常量 默认修饰符 : public static final
int YEARS = 2;
// 抽象方法 默认修饰符 : public abstract
void drugDetection();
}
/*
public interface DrugDetactable {
// 常量
public static final int YEARS = 2;
// 抽象方法
public abstract void drugDetection();
}
*/
public interface Guidable {
// 常量
// 抽象方法
void blindGuiding();
}
定义三个具体的接口实现类 :
// 子类如果继承了抽象类, 必须要重写父类中的抽象方法.
// 接口是用来给类进行 实现
的. 不是继承. 实现请使用 implements
关键字
public class Dog extends Animal implements Guidable, DrugDetectable {
@Override
public void shout() {
System.out.println("汪汪汪...");
}
@Override
public void eat() {
System.out.println("跟我混, 我骨头吃...");
}
// Dog 类必须实现 Guidable 接口中的抽象方法
// 重写 : 修饰权限必须要大于等于被重写方法的修饰权限.
public void blindGuiding() {
System.out.println(getName() + " 正在执行导盲的功能...");
}
@Override
public void drugDetection() {
System.out.println("经过 " + DrugDetectable.YEARS + " 年的训练, " + getName() + "具备了吸毒的能力, 正在执行缉毒的功能...");
}
}
public class Cat extends Animal implements Guidable {
// 传智播客 : 科技喵喵喵
@Override
public void shout() {
System.out.println("喵喵喵...");
}
@Override
public void eat() {
System.out.println("跟我混, 有鱼翅吃...");
}
// 实现接口中的抽象方法
@Override
public void blindGuiding() {
System.out.println(getName() + " 正在执行导盲的功能...");
}
}
public class Wolf extends Animal implements DrugDetectable {
@Override
public void shout() {
System.out.println("嗷嗷嗷...");
}
@Override
public void eat() {
System.out.println("跟我混, 有肉吃...");
}
// 实现接口中的抽象方法
@Override
public void drugDetection() {
System.out.println("经过 " + DrugDetectable.YEARS + " 年的训练, " + getName() + "具备了吸毒的能力, 正在执行缉毒的功能...");
}
}
测试方法 :
public class InterfaceDemo {
public static void main(String[] args) {
/*
* 需求 :
* 1. Dog 类的对象应该具备 导盲和缉毒
的功能.
* 2. Cat 类的对象应该具备 导盲
的功能.
* 3. Wolf 类的对象应该具备 缉毒
的功能.
*
* 经过我们的分析, 方法中有重复的名称.
* 1. 抽取为方法 / 函数.
* 2. 抽取类继承关系.
* 3. 抽取为接口关系. 接口中的方法是用来给所有想拥有该方法的类定义 行为规范
的.
*
* 抽象类中的抽象方法是给子类定义行为规范的.
*
* 步骤一 : 定义一个缉毒接口.
* 步骤二 : 定义一个导盲接口.
* 步骤三 : 让Dog类实现了导盲的接口, 并重写了接口的抽象方法.
*/
// 如果一个类实现了某个接口,那么这个类就拥有该接口中的所有方法.
// 1. 创建一个 Dog 类
Dog dog = new Dog();
dog.setName("哮天犬");
dog.blindGuiding();
dog.drugDetection();
// 2. Cat
Cat cat = new Cat();
cat.setName("加菲猫"); // 要节约用水, 尽量和女友一起洗澡.
cat.blindGuiding();
// 3. Wolf
Wolf wolf = new Wolf();
wolf.setName("灰太狼");
wolf.drugDetection();
}
}
5.2 多态在接口中的体现 (缉毒方法的设计)
public class InterfaceDemo {
public static void main(String[] args) {
// 接口和多态的关系 :
// 1. 接口引用指向实现类对象也是多态.
// Dog 类继承 Animal , 实现 Guidable, DrugDetectable 接口.
/*
Animal a = new Dog();
// 接口中的多态体现 (不常见)
Guidable g = new Dog();
DrugDetectable d = new Dog();
DrugDetectable d2 = new Wolf();
*/
Dog d = new Dog();
d.setName("哮天犬");
drug(d);
Wolf w = new Wolf();
w.setName("灰太狼");
drug(w);
}
// 接口在方法中的设计 : 参数类型 DrugDetectable 接口类型. 翻译: 给我一个能 `缉毒` 的对象.
// 接口类型可以接收任何实现了该接口的实现类对象.
public static void drug(DrugDetectable d) {
d.drugDetection();
}
/*
// 方法的设计
public static void drug(Dog d) {
d.drugDetection();
}
public static void drug(Wolf w) {
w.drugDetection();
}
*/
}
5.3 接口的练习
BabySitter 接口设计 :
public interface BabySitter {
// 常量 public static final
// 抽象方法 public abstract
void feedTheBaby();
void coaxTheBabyToSleep();
}
GoodBabySitter 接口设计 :
// 接口和接口之间是 继承
关系, 使用 extends 关键字实现
public interface GoodBabySitter extends BabySitter {
// 增加一个额外的方法
void changeTheDiaper();
}
Singable 接口设计 :
public interface Singable {
// teach sb to sth
void teachBabyToSing();
}
Auntie 类 :
// 问题 : 如果 Auntie 类有一个 抽象方法没有实现, 会发生什么情况 ?
// 如果一个类有抽象方法, 该类必须被定义为 抽象类
. 抽象类不能实例化对象.
// 为了能够实例化对象, 那么该类就必须要重写所有的抽象方法, 一个都不能少.
// 总结 : 如果一个类实现了接口, 那么该类就拥有了接口中的所有方法.
public class Auntie implements GoodBabySitter, Singable {
@Override
public void feedTheBaby() {
System.out.println("保姆阿姨正在喂宝宝吃东西...");
}
@Override
public void coaxTheBabyToSleep() {
System.out.println("保姆阿姨正在哄宝宝睡觉...");
}
@Override
public void teachBabyToSing() {
System.out.println("保姆阿姨正在教宝宝唱儿歌...");
}
@Override
public void changeTheDiaper() {
System.out.println("保姆阿姨正在给宝宝换尿不湿...");
}
}
测试类 :
public class InterfaceDemo {
public static void main(String[] args) {
// 1. 面向对象调用
Auntie aunt = new Auntie();
aunt.feedTheBaby();
aunt.coaxTheBabyToSleep();
aunt.teachBabyToSing();
aunt.changeTheDiaper();
System.out.println("-------------------");
// 2. 面向接口调用
// 接口类型可以接收任何实现了该接口的实现类对象, 这也是多态的表现形式.
/*
* 多态 :
* 1. 父类引用指向了子类对象.
* 2. 接口引用指向了实现类对象.
*/
GoodBabySitter sitter = new Auntie();
sitter.changeTheDiaper();
sitter.coaxTheBabyToSleep();
sitter.feedTheBaby();
// 强制向下转型
Singable s = (Singable) sitter;
s.teachBabyToSing();
}
}
6.4接口与类的关系总结 :
类和类 : 继承关系. 只能 单继承
. 使用 extends 关键字实现.
类和接口 : 实现关系. 可以 多现实
. 使用 implements 关键字实现.
接口和接口 : 继承关系. 可以 多继承
. 使用 extends 关键字实现.
7.面向接口编程
耦合性概念 :
6.1 实现方式一 :
Computer 类的设计 :
public class Computer {
// 属性
// 行为
public void run() {
System.out.println("Computer run ...");
}
// 面向对象的方法设计产生的问题 : 有多少个外接设备就需要定义多少个使用的行为.
// 修改源代码 : 拆电脑
/*public void useMouse(Mouse m) {
m.open();
m.close();
}*/
public void useKeyboard(Keyboard k) {
if (k != null) {
k.open(); // NullPointerException 空引用异常
k.close();
}
}
}
Keyboard 类的设计 :
public class Keyboard {
// 属性
// 行为
public void open() {
System.out.println("Keyboard open ...");
}
public void close() {
System.out.println("Keyboard close ...");
}
}
测试类的编写 :
/*
* 需求 : 实现笔记本电脑运行.
* 实现方式一 : 面向打印 (sysout) 编程.
* 实现方式二 : 面向对象 (Computer) 编程.
* 新需求 : 买一个鼠标, 并且让笔记本笔记本电脑使用鼠标.
* 新需求 : 买一个键盘, 并且让笔记本笔记本电脑使用键盘.
* 外接设备 : U盘, 硬盘, 麦克风, 摄像头, 小风扇 …
*/
public class ComputerDemo {
public static void main(String[] args) {
// 1. 买了一台电脑
Computer c = new Computer();
c.run();
// 2. 买了一个鼠标
// Mouse m = new Mouse();
// 问题 : 如何让电脑使用鼠标 ???
// 修改 Computer 类的源代码, 类似于生活中的拆电脑.
// c.useMouse(m);
Keyboard k = new Keyboard();
// 修改 Computer 类的源代码, 再拆一次电脑.
c.useKeyboard(k);
}
}
6.2 实现方式二 :
笔记本是一个生产厂商. 鼠标是另外一个生产厂商.笔记本要使用外接的设备,笔记本上就要预留一些插口,而这些插口的预留大小和规则,一定要遵守某个规定,这时生产的笔记本才可以使用外接设备.而外接设备在生产的时候也应该遵守一定的规则,然后生产符合规则的设备.这样笔记本和外接设备之间就可以进行通信.
public class Computer {
// 属性
// 行为
public void run() {
System.out.println("Computer run ...");
}
// 考虑外接设备使用的问题!
// 外接设备是啥啊 ??? 不能问.
// 总结 : 如果一个方法的设计参数是接口类型, 那么真正需要是该接口的实现类对象. 因为接口是不存在对象的. 只有实现类才存在对象.
public void useUSB(USB usb) {
if (usb != null) {
usb.open();
usb.close();
}
}
}
public interface USB {
void open();
void close();
}
public class Mouse implements USB {
@Override
public void open() {
System.out.println("Mouse open ...");
}
@Override
public void close() {
System.out.println("Mouse close ...");
}
}
public class Keyboard implements USB {
// 属性
// 行为
@Override
public void open() {
System.out.println("Keyboard open ...");
}
@Override
public void close() {
System.out.println("Keyboard close ...");
}
}
public class CameraVideo implements USB {
@Override
public void open() {
System.out.println("CameraVideo open ...");
}
@Override
public void close() {
System.out.println("CameraVideo close ...");
}
}
测试类 :
public class ComputerDemo {
public static void main(String[] args) {
// 1. 买了一台电脑
Computer c = new Computer();
c.run();
// 2. 买了一个鼠标
Mouse m = new Mouse();
// 问题 : 电脑如何使用鼠标 ???
// 解决方案 : 不是使用鼠标, 而是使用 USB.
// USB 是一个接口, Mouse 是该接口的实现类对象.
// 多态 : 接口类型可以接收任何实现了该接口的实现类对象.
c.useUSB(m);
Keyboard k = new Keyboard();
c.useUSB(k);
CameraVideo cv = new CameraVideo();
c.useUSB(cv);
}
}
8.匿名内部类
7.1 综合案例一 : 电脑与USB接口
在前面多态和接口的作用描述案例中,关于电脑和USB的故事.我们讲解了如果方法的参数被定义为接口类型.那么就需要定义一个类来实现接口,并根据该类进行对象实例化.除此之外,还可以使用匿名内部类来实现接口.
public class Computer {
// 属性
// 行为
public void run() {
System.out.println("Computer run ...");
}
// 面向接口的方法设计
public void useUSB(USB usb) {
if (usb != null) {
usb.open();
usb.close();
}
}
}
// 外部类定义方式
public class Mouse implements USB {
@Override
public void open() {
System.out.println("Mouse open ...");
}
@Override
public void close() {
System.out.println("Mouse close ...");
}
}
public class AnonymousClassDemo {
public static void main(String[] args) {
// 案例 : 笔记本电脑使用 USB 连接外接设备
Computer c = new Computer();
c.run();
// 使用鼠标 : 鼠标是一个类, 如何定义 ??? 方式一 : 外部类定义
Mouse m = new Mouse();
c.useUSB(m);
// new Mouse(); 匿名对象
// c.useUSB(new Mouse());
// 使用键盘 : 键盘也是一个类, 如何定义 ??? 方式二 : 方法内部类定义
class Keyboard implements USB {
@Override
public void open() {
System.out.println("Keyboard open ...");
}
@Override
public void close() {
System.out.println("Keyboard close ...");
}
}
// 创建一个 `Keyboard` 类的对象
Keyboard k = new Keyboard();
c.useUSB(k);
// 使用摄像头 : 摄像头也是一个类, 如何定义 ??? 方式三 : 匿名内部类定义
// 匿名内部类 : 该类没有名字.
/*
* 问题1 : useUSB(USB usb); 方法参数为 USB 接口类型, 之前的 m, k 对象为什么可以传入 ??? 因为他们实现了 USB 接口.
* 问题2 : 只要一个类实现了 USB 接口, 是不是就可以传入方法, 作为参数 ??? 肯定 + 绝对的.
* 问题3 : new USB(); 这是什么意思 ??? 创建一个接口的对象, 正确不正确 ? 错误的, 因为接口不能实例化对象.
* 说明 : new USB(){} 这不是创建接口的对象, 而是创建该接口的实现类对象. 内部需要重写所有的抽象方法. 这就是接口的匿名实现类.
*/
// 说明 : 只要拥有实现体, 那么其就是该接口的实现类.
// new USB(){} 这就是 USB 接口的实现类对象, 请问: 该实现类有名称吗 ? 没有, 因此该实现类被称为 `匿名实现类`.
c.useUSB(new USB(){
@Override
public void open() {
System.out.println("CameraVideo open ...");
}
@Override
public void close() {
System.out.println("CameraVideo close ...");
}
});
}
// public void shout(); 这个方法是抽象的.
// public void shout() {} 这就是一个具体的. 只要拥有方法体就是一个具体的方法.
}
7.2 综合案例二 : Animal与抽象shout()方法
有一个Animal的抽象父类,其中有shout()抽象方法.在主类中有一个动物之声的函数,其参数为Animal类型的对象.需要使用匿名内部实现动物之声方法的调用.
public abstract class Animal {
// 属性
// 行为
// 定义了一个抽象方法
public abstract void shout();
}
// 外部类
public class Dog extends Animal {
@Override
public void shout() {
System.out.println("汪汪汪 ...");
}
}
public class AnimalDemo {
public static void main(String[] args) {
// 调用函数
Dog d = new Dog();
theSoundOfAnimals(d);
// 方法内部类实现
class Cat extends Animal {
@Override
public void shout() {
System.out.println("喵喵喵 ...");
}
}
// 创建一个 Cat 对象
Cat c = new Cat();
theSoundOfAnimals(c);
// 方式三 : 匿名内部类实现方法的调用
/*
* 问题1 : theSoundOfAnimals(Animal a); 参数列表是一个父类类型, 可以接收子类对象.
* 问题2 : 为什么 d, c 对象可以传入 ??? 因为 d, c 都是 Animal 类的子类对象.
* 问题3 : 请问, 只要是 Animal 类的子类对象都可以传入该方法作为参数吗 ??? 绝对 + 肯定.
* 问题4 : new Animla(); 什么意思 ??? 创建一个 Animal 对象, 正确吗??? 不正确. 因为 Animal 是一个抽象类, 不能实例化对象.
* 说明 : new Animal(){} 这是在创建一个 Animal 类型的子类对象, 请问: 该子类对象有名字吗? 没有, 因此被称为 `匿名子类对象`.
*
* 记住 : 只要 new 之后有方法体, 那么就不再是该类了, 如果 new 之后是接口 -> `匿名实现类`, 如果是 new 之后是抽象类 -> `匿名子类`.
*/
// new Animal(){} 只要一个类拥有了实现体, 就不再称为是 `该类对象了`, 而被称为该类的 `匿名子类对象`.
theSoundOfAnimals(new Animal(){
@Override
public void shout() {
System.out.println("嗷嗷嗷 ...");
}
});
}
// 动物之声的函数 : 参数列表接收一个 Animal 类型
public static void theSoundOfAnimals(Animal a) {
a.shout();
}
}
7.3 关于匿名内部类的面试题 :
public class InterviewDemo {
public static void main(String[] args) {
// 创建一个 Object 类的子类对象
// 说明 : 只要 new 了一个对象, 左边没有引用接收, 那么这个对象就是一个 `匿名对象`.
new Object(){
public void show() {
System.out.println("show run...");
}
}.show();
// 多态 : Object 类型父类引用接收了 Object子类对象
// 编译器认为 obj 是 Object类型的.
Object obj = new Object(){
public void show() {
System.out.println("show run...");
}
};
// 去 Object 类中寻找 show() 方法 ??? 找不到, 编译报错!
// obj.show(); 错误!
// 向下转型 ??? 匿名对象没有办法向下转型.
// (名字的类型) obj;
}
}
9.Object 类 (祖宗类)
定义了一个Person类,然后直接创建了2个Person对象,如果Person类两个对象的的姓名和年龄都相同,我们就认为是同一个人,调用equals应该返回true.
8.1 equals() 方法详解
说明 : 对象的判断不应该使用 == 符号实现, 而应该调用对象其相对应的方法实现判断.
需求 : 定义一个 Person 类, 如果Person类的对象的 姓名, 年龄
都相等, 那么我认为这两个对象就应该是同一个对象.
public class Person {
// 属性
private String name;
private int age;
// 构造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 重写 equals 方法的目的 : 自定义对象的判断标准
// 开发中几乎所有的类判断标准都需要自定义.
@Override
public boolean equals(Object obj) {
// System.out.println("---------------");
// System.out.println("this = " + this);
// System.out.println("obj = " + obj);
// 1. 如果两个对象的内存地址相等, 那么就是同一个对象, 应该返回 true
if (this == obj) {
return true;
}
// 2. 如果两个对象的内存地址不相等, 此时需要再次比较对象的 `姓名和年龄` 是否都相同
if (!(obj instanceof Person)) {
// 条件成立, 说明 obj 对象不是 Person 类型的对象, 直接返回 false
return false;
}
// 3. 需要将 Object obj 转换为 Person 类型的对象 (向下转型)
// 程序如果能够来到这里, 说明 obj 对象就是Person类型的对象, 直接向下转型即可.
Person p = (Person) obj;
// 比较两个对象的姓名和年龄
// String 是什么类型 ??? 是一个引用类型. String 类的 equals 调用哪里去了 ???
// String类也重写 Object 的 equals 方法, 实现了根据字符串内容来判断是否相等.
if (this.name.equals(p.name) && this.age == p.age) {
return true;
}
// 4. 否则, 返回 false
return false;
}
// 行为
// setter & getter
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
测试类 :
public class ObjectDemo {
public static void main(String[] args) {
// 需求 : 定义一个 Person 类, 如果Person类的对象的 `姓名, 年龄` 都相等, 那么我认为这两个对象就应该是同一个对象.
Person p1 = new Person("张三", 18);
Person p2 = new Person("李四", 28);
// System.out.println("p1 = " + p1);
// System.out.println("p2 = " + p2);
// 对象的相等判断应该调用 Object 类中定义的 equals 方法实现.
// 问题 : 产生的结果和我们预期的不一致, 怎么办呢 ??? 查看 Object 类的 equals 方法的源代码.
// 需求 : `姓名, 年龄` 都相等, 那么我认为这两个对象就应该是同一个对象. (自定义的判断标准)
// 解决方案 : 重写 Object 类的 equals 方法, 实现自定义对象的判断
boolean result = p1.equals(p2);
System.out.println(result); // true
// 判断
// boolean result = p1 == p2;
// System.out.println(result);
}
}
如果两个对象的姓名和年龄完全相等, 那么结果为 :
Person p1 = new Person(“张三”, 18);
Person p2 = new Person(“张三”, 18);
说明 : Object 类的 equals 方法实现源码如下 :
8.2 toString() 方法详解
Person 类中的 toString() 方法重写代码 :
// 重写 toString() 的目的 : 自定义对象的输出信息 (查看数据)
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
测试类中的代码 :
public class ObjectDemo {
public static void main(String[] args) {
// 需求 : 定义一个 Person 类, 如果Person类的对象的 `姓名, 年龄` 都相等, 那么我认为这两个对象就应该是同一个对象.
Person p1 = new Person("张三", 18);
Person p2 = new Person("李四", 38);
// cn.itcast.object.Person 包名+类名 (全限定名称) @ 2e739136 (十六进制的地址内存字符串)
// 如果在程序中直接 `输出一个对象`, 其实就相当于调用了该对象的 toString() 方法.
// 问题 : 如果输出一个对象, 你最希望看到的结果是什么 ??? 是这个对象的所有数据. 数据是存储在属性中的.
// 解决方案 : 重写 Object 类的 toString() 方法. 实现自定义数据的输出.
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
}
}
说明 : Object 类的 toString() 方法实现源码如下 :
10.静态与非静态的设计 (属性与方法)
public class Student {
// 属性 (存储数据)
// 思考 : 如果该数据每个对象都不一样, 那么该属性必须为 非静态属性
. 非静态在堆内存中每个对象都有自己的一份.
private String name;
private int age;
private char gender;
// 定义一个 PI
// 思考 : 静态属性属于该类的. 直接用类名访问, 与对象无关, 不会在堆区中开辟空间, 在内存中仅有一份. (节省内存空间)
public static final double PI = 3.14;
// 行为 :
// introduce 方法中使用到了 `成员属性`, 成员属性是属于 `对象`
// 小结 : 如果一个方法中使用到了成员属性, 那么该方法就必须定义为 `非静态 / 对象` 方法
public void introduce() {
// this -> stu, stu2
System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
}
// 需求1 : 求长方形的面积 公式: 宽 * 高
// 该方法没有使用任何 `成员属性`, 那么该方法就应该被定义为 `静态方法`
public static int getAreaOfRect(int width, int height) {
return width * height;
}
// 需求2 : 求圆的面积 公式: π * r * r -> Radius 半径
public static double getAreaOfCircle(int radius) {
// 修改 PI 的值
// PI = 2.14;
return PI * radius * radius;
}
// setter & getter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
测试一 :
public class StudentDemo {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("张三");
stu.setAge(19);
stu.setGender('男');
stu.introduce();
Student stu2 = new Student();
stu2.setName("李四");
stu2.setAge(20);
stu2.setGender('女');
stu2.introduce();
}
}
测试二 :
public class StudentDemo2 {
public static void main(String[] args) {
// 需求 : 让 学生 算出长方形的面积
/*
Student stu = new Student();
int rect = stu.getAreaOfRect(3, 5);
System.out.println(rect);
*/
// 需求 : 让 学生 算出长方形的面积
int rect = Student.getAreaOfRect(3, 5);
System.out.println(rect);
// 需求 : 让学生算出圆的面积
double circle = Student.getAreaOfCircle(8);
System.out.println(circle);
}
}
11.访问权限修饰符
private < 默认 < protected < public
public class Animal {
// 属性
protected String name;
protected int age;
// 行为
/*public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}*/
}
public class Dog extends Animal {
// 属性
// 行为
// 特有行为 :
public void show() {
System.out.println("name = " + name + ", age = " + age);
// System.out.println("name = " + getName() + ", age = " + getAge());
}
}
import cn.itcast.pkg2.Animal;
public class Cat extends Animal {
// 属性
// 行为
// 特有行为 :
public void show() {
System.out.println("name = " + name + ", age = " + age);
// System.out.println("name = " + getName() + ", age = " + getAge());
}
}
测试类 :
/*
* 访问权限修饰符 : private < 默认 < protected < public
*/
public class AnimalDemo {
public static void main(String[] args) {
Animal a = new Animal();
a.name = "动物";
// a.setName("动物");
}
}
12.异常类 Exception
1.编译时异常. 特点: 不管正确与否, 都需要进行处理.
a)声明. throws 异常类型. 一旦发生异常, 程序就会异常终止.
b)捕获. try-catch-finally, 不管是否发生异常, 程序都会正常终止.
2.运行时异常. 特点: 不管正确与否, 都可以不处理.
a)不处理. 结果: Java虚拟机会提前异常终止程序.
b)处理. 捕获异常. 正常可以正常终止.
异常发生后的执行过程 :
请问为什么Java语言要设计异常类 : 提醒机制
9.1 如何手动抛出异常: throw new 异常对象();
/*
* 知识点 : 如何抛出异常 ???
*
* 1. NullPointerException 空引用异常
* 2. ArrayIndexOutOfBoundsException 数组下标越界异常.
*/
public class ExceptionDemo1 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = new int[]{10, 20, 30};
// 2. 调用方法
int element = getElement(arr, 2); // getElement 方法发生是运行时异常.
System.out.println(element);
System.out.println("程序正常终止......");
}
// 方法 : 从数组中获取对应下标的元素
public static int getElement(int[] array, int index) {
// 1. 判断 array 是否为空
if (array == null) {
// 发生了空引用异常
// throw 异常对象();
throw new NullPointerException("程序发生了空引用异常. 数组引用 array 不能为空.");
}
// 2. 判断 index 是否越界
if (index < 0 || index >= array.length) {
// 发生了数组下标越界异常
throw new ArrayIndexOutOfBoundsException(index + " 下标越界, 非法访问.");
}
int element = array[index];
return element;
}
}
9.2 运行时异常的处理方案1 : 不处理
一旦发生异常, 程序就会异常终止.
9.3 运行时异常的处理方案2 : 捕获
代码一 :
/*
* 知识点 : 如何抛出异常 ???
*
* 1. NullPointerException 空引用异常
* 2. ArrayIndexOutOfBoundsException 数组下标越界异常.
*/
public class ExceptionDemo2 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = new int[]{10, 20, 30};
// 2. 调用方法
// 运行时异常的处理一方案 : 不处理 产生的结果 : 程序异常终止.
// 运行时异常的处理二方案 : 处理 捕获异常
/*
* try {
*
* } catch (异常类型 e) {
*
* }
*/
try {
// 尝试执行的代码, 可能会有异常发生.
int element = getElement(arr, 3);
System.out.println(element);
} catch (NullPointerException e) {
System.out.println("发生了空引用异常...");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("发生数组下标越界异常...");
}
System.out.println("程序正常终止......");
}
// 方法 : 从数组中获取对应下标的元素
public static int getElement(int[] array, int index) {
// 1. 判断 array 是否为空
if (array == null) {
// 发生了空引用异常
// throw 异常对象();
throw new NullPointerException("程序发生了空引用异常. 数组引用 array 不能为空.");
}
// 2. 判断 index 是否越界
if (index < 0 || index >= array.length) {
// 发生了数组下标越界异常
throw new ArrayIndexOutOfBoundsException(index + " 下标越界, 非法访问.");
}
int element = array[index];
return element;
}
}
代码二 :
/*
* 知识点 : 如何抛出异常 ???
*
* 1. NullPointerException 空引用异常
* 2. ArrayIndexOutOfBoundsException 数组下标越界异常.
*/
public class ExceptionDemo3 {
public static void main(String[] args) {
// 1. 定义一个数组
int[] arr = new int[]{10, 20, 30};
// 2. 调用方法
// 运行时异常的处理一方案 : 不处理 产生的结果 : 程序异常终止.
// 运行时异常的处理二方案 : 处理 捕获异常
/*
* try {
*
* } catch (异常类型 e) {
*
* }
*/
try {
// 尝试执行的代码, 可能会有异常发生.
int element = getElement(null, 3);
System.out.println(element);
} catch (RuntimeException e) {
// getMessage
// String message = e.getMessage();
// toString();
// String str = e.toString();
e.printStackTrace(); // 打印输出堆区的跟踪信息
System.err.println("发生运行时异常...");
}
int num1 = 10;
int num2 = 20;
int sum = num1 + num2;
System.out.println(sum);
System.out.println("程序正常终止......");
}
// 方法 : 从数组中获取对应下标的元素
public static int getElement(int[] array, int index) {
// 1. 判断 array 是否为空
if (array == null) {
// 发生了空引用异常
// throw 异常对象();
throw new NullPointerException("程序发生了空引用异常. 数组引用 array 不能为空.");
}
// 2. 判断 index 是否越界
if (index < 0 || index >= array.length) {
// 发生了数组下标越界异常
throw new ArrayIndexOutOfBoundsException(index + " 下标越界, 非法访问.");
}
int element = array[index];
return element;
}
}
9.4 编译时异常的处理方案1 : 声明
/*
* throws 声明异常 throws 异常类型
* throw 抛出异常 throw new 异常对象();
*/
public class ExceptionDemo1 {
public static void main(String[] args) throws IOException {
// 回顾 : 读取文件中的数据 BufferedReader -> FileReader
// 1. 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("demo1.txt"));
// 2. 一行一行读取数据
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 3. 关闭资源
reader.close();
System.out.println("程序正常终止...");
}
}
9.5 编译时异常的处理方案2 : 捕获
代码一 :
/*
* throws 声明异常 throws 异常类型
* throw 抛出异常 throw new 异常对象();
*/
public class ExceptionDemo2 {
public static void main(String[] args) {
// 回顾 : 读取文件中的数据 BufferedReader -> FileReader
try {
// 1. 创建一个高效的缓冲区文件读取对象
BufferedReader reader = new BufferedReader(new FileReader("demo1.txt"));
// 2. 一行一行读取数据
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 业务逻辑...
int i = 10 / 0; // ArithmeticException
// 3. 关闭资源
reader.close();
System.out.println("资源已关闭.");
} catch (FileNotFoundException e) {
System.out.println("文件找不到.");
} catch (IOException e) {
System.out.println("文件读写异常.");
} catch (ArithmeticException e) {
System.out.println("发生了算术异常.");
}
int num1 = 10;
int num2 = 20;
System.out.println(num1 + num2);
System.out.println("程序正常终止...");
}
}
代码二 :
/*
* throws 声明异常 throws 异常类型
* throw 抛出异常 throw new 异常对象();
*
* 1. IO 读写.
* 2. 数据库.
*
* try {
* // 可能发生异常的代码
*
* } catch (异常类型 e) {
* // 对应的业务逻辑处理
*
* } finally {
* // 关闭资源
* }
*/
public class ExceptionDemo3 {
public static void main(String[] args) {
// 调用读取文件的方法
String result = readerDataFromFile("demo1.txt");
System.out.println("程序正常终止... " + result);
}
public static String readerDataFromFile(String fileName) {
// 回顾 : 读取文件中的数据 BufferedReader -> FileReader
BufferedReader reader = null;
try {
// 业务逻辑...
int i = 10 / 0; // ArithmeticException
// 1. 创建一个高效的缓冲区文件读取对象
reader = new BufferedReader(new FileReader(fileName));
// 2. 一行一行读取数据
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
// 根据公司的业务逻辑进行编写...
e.printStackTrace();
// System.out.println("发生了异常.");
return "文件读取失败!";
} finally {
// 在 finally 块的代码, 如果 catch 是否有 return 语句, 该代码块都会被执行.
if (reader != null) {
try {
// 3. 关闭资源
reader.close(); // null.close();
System.out.println("资源已关闭.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
int num1 = 10;
int num2 = 20;
System.out.println(num1 + num2);
*/
// 文件读取成功
return "文件读取成功";
}
}
9.6 异常的细节
Person 类 :
public class Person {
// 属性
// 行为
public void introduce() {
// 故意抛出一个运行时异常
// throw 关键字之后不可以直接编写代码.
boolean flag = true;
if (flag) {
throw new RuntimeException("故意抛出一个运行时异常...");
}
System.out.println("大家好, 我是Person类.");
}
}
Inter 接口 :
public interface Inter {
// 抽象行为
void run();
}
Student类 :
public class Student extends Person implements Inter {
// 属性
// 行为
// Exception is not compatible 不兼容 with throws clause 声明语句 in Person.introduce()
// 子类重写父类的 introduce 方法与父类的该方法不兼容.
// 子类如果重写父类的方法, 父类该方法没有抛出编译时, 子类就不可以抛出编译时异常.
@Override
public void introduce() {
// 读取文件
// 子类重写父类的方法, 代码中如果发生编译时异常, 子类可以如何处理呢 ??? 子类只能捕获异常. 不能声明.
try {
BufferedReader reader = new BufferedReader(new FileReader("demo1.txt"));
} catch (FileNotFoundException e) {
// e.printStackTrace();
System.out.println("文件读取失败.");
}
}
// Inter 接口中的方法
@Override
public void run() {
// 重写接口方法时, 如果发生编译时, 必须在内部捕获, 不可以声明编译时异常.
try {
BufferedReader reader = new BufferedReader(new FileReader("demo1.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
异常细节小结 :
/*
* 异常处理的方案 :
*
* 1. throws 声明异常.
* 2. try-catch 捕获异常.
*
* 思考1 : 该异常目前代码能不能处理 ??? 教学: throws 快.
*
* JDBC Java连接数据库 -> JavaEE 开发的三层模型:
*
* 1. Web 层 网页层
* 2. Service 层 业务逻辑层 捕获异常. try-catch-finally
* 3. DAO 层 (Data Access Object 数据访问对象层) 作用: 跟数据库打交道. 异常基本都是在 DAO 层发生的. DAO 直接抛.
*/
public class PersonDemo {
}
9.7 异常思考题
public class ThinkingDemo2 {
public static void main(String[] args) {
System.out.println("1");
try {
String str2 = null;
// NullPointerException
str2.toString();
System.out.println("2");
String str1 = "";
str1.toString(); // 没有异常
System.out.println("3");
// ArithmeticException
int num = 10 / 0;
System.out.println("4");
int[] arr = {10, 20, 30};
// ArrayIndexOutOfBoundsException
System.out.println(arr[2]);
System.out.println("5");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("6");
} catch (NullPointerException e) {
System.out.println("7");
} catch (ArithmeticException e) {
System.out.println("8");
} finally {
System.out.println("9");
}
System.out.println("10");
}
}
分析 :
1.第一位不能是 0
2.QQ号码在 5 ~ 12 位之间 (包含)
3.QQ号码都是由数字组成.
// 正则表达式 : regex
public class RegularExpressionDemo1 {
public static void main(String[] args) {
// 需求 : 验证 QQ 号码是否合法
/*
分析 :
1. 第一位不能是 0
2. QQ号码在 5 ~ 12 位之间 (包含)
3. QQ号码都是由数字组成.
*/
String qq = "123456789123"; // 网页文本框 : 13345679876 (字符串) QQ号码: 123456 (字符串)
boolean result = verifyQQNumber(qq);
System.out.println(result);
result = verifyQQNumberUsingRegex(qq);
System.out.println(result);
}
// 使用正则表达式来验证QQ号码是否合理 正则其实就是 `规则字符串`.
private static boolean verifyQQNumberUsingRegex(String qq) {
/*
* 1. 中括号 : 中括号定义内容的范围:
* [1-9] [a-z] [A-Z] [12345]
* [0-9] \\d
*
* 2. 大括号 : 定义前一个内容可以出现的次数.
* {4,11} 最少 4 次, 最多 11 次.
* {4,}; 最少 4 次, 最多无限次.
* {4} 只能4次.
*/
return qq.matches("[1-9]\\d{4,11}");
}
private static boolean verifyQQNumber(String qq) {
// 1. 第一位不能是 0
// qq.startsWith("0")
if (qq.charAt(0) == '0') {
System.out.println("第一位不能为 0.");
return false;
} else if (qq.length() < 5 || qq.length() > 12) {
// 2. QQ号码在 5 ~ 12 位之间 (包含)
System.out.println("QQ号码在 5 ~ 12 位之间 (包含).");
return false;
} else {
// 3. QQ号码都是由数字组成.
// 遍历字符串, 取出每一个字符, 判断范围.
// int类型数值 Integer.parseInt(字符串) 声明了一个异常: NumberFormatException 数字格式化异常
// long类型数值 Long.parseLong(字符串)
// 方式一 : 不处理
// 方式二 : 捕获
try {
Long.parseLong(qq);
return true;
} catch (NumberFormatException e) {
// QQ号码都是由数字组成
System.out.println("QQ号码都是由数字组成.");
return false;
}
}
}
}
1.2、切割 – split (字符串)
1.String str = “boxing#######basketball#####football###ILOVEYOU###爱我中华”;
2.
3.String str = “boxing123basketball4567football888ILOVEYOU9876爱我中华”;
4.
5.boxing
6.basketball
7.football
8.ILOVEYOU
9.爱我中华
public class RegularExpressionDemo2 {
public static void main(String[] args) {
// String str = "boxing+++++basketball+football++ILOVEYOU+++爱我中华";
String str = "boxing123basketball4567football888ILOVEYOU9876爱我中华";
splitString(str);
}
public static void splitString(String str) {
// 切割
/*
* 数量词 :
* + 一次或更多
* ? 0次或一次
* * 0次或更多
*/
// String[] strArr = str.split("\\++"); // \\+
// String[] strArr = str.split("#+");
String[] strArr = str.split("\\d+"); // \\d
for (int i = 0; i < strArr.length; i++) {
String s = strArr[i];
System.out.println(s);
}
}
}
1.3、替换 – replaceAll (隐藏手机号码)
1.13888888888 -> 138****8888
需求:验证手机号码 :
1.手机号码规则 :
2.1. 长度必须是11位
3.2. 第一位只能是数字1
4.3. 第二位可以是3, 4, 5, 7, 8
5.4. 从第三位开始可以是 0-9
public class RegularExpressionDemo3 {
public static void main(String[] args) {
// 13345679876 -> 133****9876
String telphone = "13366668888"; // "18345679878";
// 隐藏 :
String newPhonenumber = hideTelphoneNumber(telphone);
System.out.println(newPhonenumber);;
// 验证 :
boolean result = verifyTelphoneNumber(telphone);
System.out.println(result);
}
// 定义一个方法, 隐藏手机号码
private static String hideTelphoneNumber(String telphone) {
// 13345679876 -> 133****9876
// 分组 : ()()()
// 133 (1[34578]\\d) 4567 (\\d{4}) 9876 ([0-9]{4})
// 正则字符串:
// 参数1 : 正则字符串 (1[34578]\\d)(\\d{4})([0-9]{4})
// 参数2 : 替换的内容 $1 $2 $3
// String replaceAll(String regex, String replacement)
// String newTelphoneNumber = telphone.replaceAll("(1[34578]\\d)(\\d{4})([0-9]{4})", "$1$3$2");
String newTelphoneNumber = telphone.replaceAll("(1[34578]\\d)(\\d{4})([0-9]{4})", "$1****$3");
return newTelphoneNumber;
}
/*
手机号码规则 :
1. 长度必须是11位
2. 第一位只能是数字1
3. 第二位可以是3, 4, 5, 7, 8
4. 从第三位开始可以是 0-9
*/
public static boolean verifyTelphoneNumber(String telphone) {
return telphone.matches("1[34578]\\d{9}");
}
}
13.常用类介绍
1.System 系统类
public class SystemDemo {
public static void main(String[] args) {
// new Scanner(System.in);
// System.out.println(“hello world!”);
// System.err.println(“你好, 世界!”);
// CPU 内存 硬盘
// static long currentTimeMillis() 返回以毫秒为单位的当前时间。
long start = System.currentTimeMillis();
// 在栈区中执行循环
long sum = 0;
for (int i = 0; i < 1000000000; i++) {
// System.out.println("hello 你好!");
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("共耗时: " + (end - start) + " 毫秒.");
}
}
2.Math 数学类
public class MathDemo {
public static void main(String[] args) {
// 1. abs 绝对值 (正值)
System.out.println(Math.abs(-88)); // 88
System.out.println(Math.abs(88)); // 88
// 2. ceil
// 3. floor
// 4. round
System.out.println(Math.ceil(88.1f)); // 89.0
System.out.println(Math.floor(88.9f)); // 88.0
System.out.println(Math.round(88.5f)); // 89
// 5. max
// 6. min
System.out.println(Math.max(20, 30)); // 30
System.out.println(Math.min(20, 30)); // 20
// 7. pow
System.out.println(Math.pow(10, 3)); // 1000
// 8. random
System.out.println(Math.random()); // 0.0 ~ 1.0 之间的double类型值
// 需求 : 多线程 模拟耗时 0.0 ~ 1.0
// Thread.sleep((long)(Math.random() * 1000)); 0.578 0.012
}
}
3.Random 随机类
public class RandomDemo {
public static void main(String[] args) {
// Random();
// Random r = new Random(998); 当前时间毫秒
// Random(long seed);
Random r = new Random();
for (int i = 0; i < 10; i++) {
// int nextInt();
// int result = r.nextInt();
// int nextInt(int n);
// 需求 : 0 ~ 10 之间
int result = r.nextInt(31) + 20; // 20 ~ 50
System.out.println(result);
}
}
}
4.Arrays 数组工具类
public class ArraysDemo {
public static void main(String[] args) {
// util -> Utility 工具 (工具类中的方法基本全是 static 修饰的)
// Arrays 数组工具类 :
// toString 转字符串
int[] arr = {10, 20, 30, 40, 50};
System.out.println(arr); // [I@5cfe174
String result = Arrays.toString(arr);
System.out.println(result); // [10, 20, 30, 40, 50]
// sort 排序
int[] arr2 = {34, 23, 12, 89, 65, 55};
System.out.println(Arrays.toString(arr2));
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2)); // [12, 23, 34, 55, 65, 89]
// 需求 : 字符串进行判断
String s = "yuhfnsmazqwicx";
char[] chArr = s.toCharArray();
Arrays.sort(chArr);
System.out.println(chArr); // acfhimnqsuwxyz
System.out.println(Arrays.toString(chArr)); // [a, c, f, h, i, m, n, q, s, u, w, x, y, z]
}
}
import java.util.Date;
public class DateDemo1 {
public static void main(String[] args) {
// 1970 年 1 月 1 日 00:00:00 GMT
// 1970 年 1 月 1 日 08:00:00 CST 东八区的标准时间
// 1. 创建一个日期类对象
Date date = new Date();
System.out.println(date);
// 2. 传入毫秒数, 从标准时间开始计算
Date date2 = new Date(1000 * 60 * 60 * 24);
System.out.println(date2);
// 3. getTime();
long time = date.getTime();
System.out.println(time); // 1516246224725
System.out.println(System.currentTimeMillis());
// 4. setTime(long);
// 传入毫秒数, 从标准时间开始计算
date.setTime(1000 * 60 * 60 * 24);
System.out.println(date);
}
}
public class DateFormatDemo2 {
public static void main(String[] args) {
// 1. 格式化 :(也就是日期 -> 文本/字符串) format
// String format(Date date) 将一个 Date 格式化为日期/时间字符串。
// 2. 解析 :(文本/字符串-> 日期) parse
// Date parse(String source) 从给定字符串的开始解析文本,以生成一个日期。
// 演示格式化 :
// formatMethod();
// 演示解析 :
parseMethod();
}
private static void parseMethod() {
// 注意点 : 在创建日期格式化对象的同时需要告诉该对象解析的格式.
// pattern -> Pattern 正则对象
// pattern -> 解析字符串的解析模式
// SimpleDateFormat(String pattern) 用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
String source1 = "2018年01月18日 08:08:08";
String source2 = "2018-1-18 08:08:08";
String source3 = "2018/1/18 08:08:08";
String source4 = "2018 1 18 08:08:08";
// ParseException 解析异常
try {
Date date = df.parse(source1);
System.out.println(date);
} catch (ParseException e) {
// e.printStackTrace();
System.out.println("字符串转日期解析格式异常!");
}
}
private static void formatMethod() {
// 需求 : 创建一个日期格式化对象
DateFormat df = new SimpleDateFormat();
// 格式化 : 18-1-18 上午11:40
// 日-月-年 上午:时间
String format = df.format(new Date());
System.out.println(format);
}
}
import java.util.Calendar;
public class CalendarDemo3 {
public static void main(String[] args) {
// 需求 : 创建一个 `日历` 对象
Calendar c = Calendar.getInstance();
// 1. 获取 `年`
int year = c.get(Calendar.YEAR);
// 2. 获取 `月` 0~11
int month = c.get(Calendar.MONTH);
// 3. 获取 `日`
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println("今天是 " + year + "年" + (month + 1) + "月" + day + "日");
// 需求1 : 10 年之后
c.add(Calendar.YEAR, 10);
printCalendar(c);
// 需求2 : 5个月之前
c.add(Calendar.MONTH, -5);
printCalendar(c);
// 需求3 : 回到 2008年8月8日
c.set(2008, 7, 8);
printCalendar(c);
}
private static void printCalendar(Calendar c) {
// 1. 获取 `年`
int year = c.get(Calendar.YEAR);
// 2. 获取 `月` 0~11
int month = c.get(Calendar.MONTH);
// 3. 获取 `日`
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println("今天是 " + year + "年" + (month + 1) + "月" + day + "日");
}
}
public class WrappingClassDemo1 {
public static void main(String[] args) {
/* 基本数据类型 包装类型 (对象类型) 类: 可以提供 `属性和行为`
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* char Character
* boolean Boolean
*/
// 需求1 : 请问 80 的 `二进制, 八进制, 十六进制` 如何表示 ???
System.out.println(Integer.toBinaryString(80));
System.out.println(Integer.toOctalString(80));
System.out.println(Integer.toString(80));
System.out.println(Integer.toHexString(80));
// 需求2 : int类型的最大值和最小值分别为多少 ?
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
// SIZE 该类型在内存中占用的比特位 1个字节 -> 8个比特位
System.out.println(Integer.SIZE); // 32
// TYPE 包装类型对应的基本数据类型
System.out.println(Integer.TYPE); // int
// 需求3 : 将字符串类型的 “100” 如何转换成整型的 100 ?
int num = Integer.parseInt("100");
System.out.println(num);
}
}
2.基本数据类型与包装类型之间的转换 :
public class WrappingClassDemo2 {
public static void main(String[] args) {
// 类型转换
// 1. 基本数据类型转包装类型
Integer integer_num1 = new Integer(88);
Integer integer_num2 = Integer.valueOf(998);
// 2. 包装类型转基本数据类型
int num1 = integer_num1.intValue();
// 3. 基本数据类型转字符串
String num1_str = 998 + "";
String num2_str = String.valueOf(88);
String num3_str = Integer.toString(2);
// 4. 字符串转基本数据类型
try {
int num2 = Integer.parseInt(num1_str);
} catch (NumberFormatException e) {
// TODO ...
}
// 5. 字符串转包装类型
Integer integer_num3 = new Integer("666");
Integer integer_num4 = Integer.valueOf("888");
// 6. 包装类型转字符串
String num4_str = integer_num3.toString();
}
}
public class AutoBoxingDemo2 {
public static void main(String[] args) {
// 1. 自动装箱 基本数据类型 -> 包装类型
Integer integer_num1 = 998; // Integer.valueOf(998);
// 2. 自动拆箱 包装类型 -> 基本数据类型
int num1 = integer_num1; // integer_num1.intValue();
// 基本数据类型和包装类型可以自动完成运算
int sum = integer_num1 + num1;
System.out.println(sum);
}
}
public class AutoBoxingDemo3 {
public static void main(String[] args) {
// 面试题 :
// 说明 : 没有使用自动装箱的技术
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2); // ??? false
// IntegerCache 整型包装类缓存
// 缓存范围 [-128 ~ 127] 返回的是缓存中的对象, 如果超过这个范围, 此时就会 new 一个新包装对象返回.
// Integer i3 = Integer.valueOf(127);
// Integer i4 = Integer.valueOf(127);
// 说明 : 使用自动装箱的技术
Integer i3 = 127;
Integer i4 = 127;
System.out.println(i3 == i4); // ??? true
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5 == i6); // ??? false
}
}
public class ForeachDemo {
public static void main(String[] args) {
int[] arr = new int[]{10, 20, 30, 40, 50};
// 遍历一 :
// 需求 : 针对元素3 (下标为2) 的元素增加 +=100.
for (int i = 0; i < arr.length; i++) {
if (i == 2) {
arr[i] += 100;
}
System.out.println(arr[i]);
}
System.out.println("--------------------");
// 遍历二 :
/*
* for (元素类型 变量名 : 数组 / 集合) {
* 在内部直接操作 `变量名` (元素对象)
* }
*/
// 需求 : 针对元素5 (下标为4) 的元素增加 -=100.
// 高级循环底层实现方案 : Iterator 迭代器
for (int num : arr) {
// 完成不了, 因为 `高级循环中` 没有下标, 无法获取指定元素, 如果要操作, 只能整体操作.
/*if (num == 50) {
num -= 100;
}*/
System.out.println(num);
}
// 练习 :
String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
for (String day : weeks) {
System.out.println(day);
}
}
}
public class VariableArgumentsDemo {
public static void main(String[] args) {
// 可变参数 :
// float sum = sum(10.88f, 20.88f);
// System.out.println(sum);
// 说明1 : 如果方法的参数类型是 `数组` 类型, 调用该方法的时候需要将参数手动包装为数组, 然后才可以传入.
// int[] arr = {10, 20, 30, 40, 50, 60, 70, 80, 90};
// 需求 : 希望将参数直接传入, 那么该方法参数类型必须定义为 `可变参数类型`.
// 可变参数类型的好处 : 1. 如果是多个参数,可以一个一个直接传入, 无需包装为数组. 2. 如果手动将参数包装为数组, 也可以传入.
// 如果一个方法的参数类型为可变参数, 可以一个都不传递.
// int sum = sum();
int sum = sum(10, 20, 30, 40, 50, 60, 70, 80, 90);
System.out.println(sum);
}
// 需求 : 实现多个整型求和的动能
// Duplicate method 重复的方法
// The variable argument type int of the method sum must be the last parameter
// 可变参数只能位于参数列表的最后一位.
// 可变参数类型底层就是数组.
public static int sum(int... array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
}
public class CollectionDemo2 {
public static void main(String[] args) {
// 问题1 : 请问集合存储同一类型的元素多呢 ? 还是存储不同类型的元素的多 ?
// 集合就是用来存储同一类型的元素.
// 问题2 : 集合如何来统一其存储元素的类型 ??? 泛型技术 <元素类型>
// list.add(998);
// add, contains, equals, hashCode, isEmpty, remove, clear, size, toArray.
Collection<String> c = new ArrayList<String>();
// add
c.add("林青霞");
c.add("张曼玉");
c.add("柳岩");
c.add("苍老师");
c.add("小泽玛利亚");
// contains 包含
boolean result = c.contains(998);
System.out.println(result);
// equals
// 集合底层虽然是数组, 集合 != 数组
String[] strArr = {"林青霞","张曼玉","柳岩","苍老师","小泽玛利亚"};
boolean result2 = c.equals(strArr);
System.out.println(result2);
// AbstractList 重写了 equals 方法, 实现根据元素内容来判断集合是否相等.
Collection<String> c2 = new ArrayList<String>();
c2.add("林青霞");
c2.add("张曼玉");
c2.add("柳岩");
c2.add("苍老师");
c2.add("小泽玛利亚");
boolean result3 = c.equals(c2);
System.out.println(result3); // true
// hashCode 哈希值
// 如果两个集合中元素内容完全相等, 那么计算出来的哈希值就 `应该` 相等.
System.out.println(c.hashCode()); // 1171943413 `HashSet`
System.out.println(c2.hashCode()); // 1171943413
// isEmpty 判断集合中元素是否为空
boolean result4 = c.isEmpty();
System.out.println(result4);
// clear 清空
c.clear();
result4 = c.isEmpty();
System.out.println(c);
System.out.println(result4);
// remove
System.out.println(c2);
boolean result5 = c2.remove("苍老师");
System.out.println(result5);
System.out.println(c2);
// size
int size = c2.size();
System.out.println(size);
for (int i = 0; i < c2.size(); i++) {
// 请问 : c2 没有 get() 方法 ???
// c2.get
}
// toArray
Object[] objs = c2.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
// Object = 引用类型;
}
}
public class CollectionDemo3 {
public static void main(String[] args) {
// Iterator 共有迭代器 (遍历器)
ArrayList<String> list1 = new ArrayList<String>();
list1.add("刘德华");
list1.add("李冰冰");
list1.add("范冰冰");
list1.add("郭富城");
list1.add("郭德纲");
// 1. 获取集合实现类的迭代器对象
Iterator<String> it = list1.iterator();
// 2. 使用迭代器对象调用 hasNext() 判断是否有下一个元素
while (it.hasNext()) {
// 2.2 如果条件成立, 说明迭代器中有下一个元素, 如何获取 ??? 使用迭代器对象调用 next() 方法获取.
String element = it.next();
System.out.println(element);
}
}
}
2.2 ListIterator 列表迭代器
共演示了三个功能 :
1.使用Iterator迭代器实现集合元素的正向迭代.
2.使用 ListIterator 迭代器实现了集合元素的迭代同是并对集合元素实现 增删改
的操作.
3.使用 ListIterator 迭代器实现了集合元素的逆向迭代, 注意获取列表迭代器时需设置光标的初始位置.
public class ListIteratorDemo {
public static void main(String[] args) {
// List 特有迭代器演示 : ListIterator 是实现了 List 接口类特有的迭代器方法.
// 需求 : 存储自定义Student对象. 柳岩, 范冰冰, 汤唯
// 1. 创建一个 ArrayList 集合, 将泛型定义为 Student
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("柳岩", 18));
list.add(new Student("范冰冰", 19));
list.add(new Student("汤唯", 16));
// Iterator 接口
Iterator<Student> it = list.iterator();
while (it.hasNext()) {
Student stu = it.next();
System.out.println(stu);
}
System.out.println("---------------------");
// 2. ListIterator 接口, 实现正向迭代的同时, 并设置集合中的元素
ListIterator<Student> listIt = list.listIterator();
while (listIt.hasNext()) {
Student stu = listIt.next();
// 需求 : 在范冰冰之后添加 `刘亦菲`, 删除 `汤唯`, 并将 `柳岩` 设置为 `赵薇`
if (stu.getName().equals("范冰冰")) {
listIt.add(new Student("刘亦菲", 12));
} else if (stu.getName().equals("汤唯")) {
listIt.remove();
} else if (stu.getName().equals("柳岩")) {
listIt.set(new Student("赵薇", 30));
}
}
System.out.println(list);
System.out.println("---------------------");
// 3. ListIterator 接口, 实现元素的逆向迭代.
// 正向迭代 : hasNext() + next
// 逆向迭代 : hasPrevious + previous
// ListIterator<Student> listIt2 = list.listIterator(); // 此时获取的 `迭代器光标` 在集合的第0个元素之前.
// 说明 : 如果要实现逆向迭代, 在获取列表迭代器的同时, 需要将光标设置到集合的最后, 参数传入 `集合对象.size();`
ListIterator<Student> listIt2 = list.listIterator(list.size()); // 此时获取的 `迭代器光标` 在集合的最后.
while (listIt2.hasPrevious()) {
Student stu = listIt2.previous();
System.out.println(stu);
}
}
}
public class CollectionDemo3 {
public static void main(String[] args) {
// Iterator 共有迭代器 (遍历器)
ArrayList<String> list1 = new ArrayList<String>();
list1.add("刘德华");
list1.add("李冰冰");
list1.add("范冰冰");
list1.add("郭富城");
list1.add("郭德纲");
// 细节一 : 迭代器应该一个 hasNext() 对应一个 next().
// NoSuchElementException 没有那个元素异常
Iterator<String> it = list1.iterator();
while (it.hasNext()) {
String element = it.next();
System.out.println(it.next());
}
}
}
注意二 :
并发
修改异常.
public class CollectionDemo3 {
public static void main(String[] args) {
// Iterator 共有迭代器 (遍历器)
ArrayList<String> list1 = new ArrayList<String>();
list1.add("刘德华");
list1.add("李冰冰");
list1.add("范冰冰");
list1.add("郭富城");
list1.add("郭德纲");
// 细节二 : 迭代器在迭代的同时, 不允许其它对象对集合中的元素进行 `增删改` 操作, 否则发生 `并发修改异常`.
// Concurrent 并发(多个对象在操作同一个数据) Modification(修改) Exception(异常)
Iterator<String> it = list1.iterator();
while (it.hasNext()) {
// 删除带 `冰冰` 的.
String str = it.next();
if (str.contains("冰冰")) {
// 删除
list1.remove(str);
// it.remove();
}
}
System.out.println(list1);
}
}
// foreach 循环 : 底层使用 `迭代器` 实现的. 迭代器没有下标, foreach 循环没有下标.
// ConcurrentModificationException : 并发修改异常!
for (String str : list1) {
if (str.contains("冰冰")) {
list1.remove(str);
}
}
System.out.println(list1);
/*
* 1. 准备牌 : 数字+花色 (拼接)
* 2. 洗牌 : Collections.shuffle(pokers);
* 3. 发牌 : for i 普通循环 if - else + % 判断
* 4. 看牌 : 直接输出集合
*
* 思考题 : 如何实现每一个玩家和底牌的 有序
排列.
*/
public class PokerDemo {
public static void main(String[] args) {
// 斗地主 : 三个玩家, 三张底牌
// 牌 : 2 3 4 5 6 7 8 9 10 J Q K A ♣ ♦ ♠ ♥ 小☺ 大☺
// 2♣ 2♦ 2♠ 2♥ 3♣ 3♦ 3♠ 3♥ ... 小☺ 大☺ (54张牌)
/*
pokers.add("2♣");
pokers.add("2♦");
pokers.add("2♠");
pokers.add("2♥");
...
*/
// 1.1 准备牌
ArrayList<String> pokers = new ArrayList<String>();
// 2♣ 2♦ 2♠ 2♥ 3♣ 3♦ 3♠ 3♥ ... 小☺ 大☺ (54张牌) 数字 + 花色
// 1.2 准备数字牌 2~10 J Q K A
ArrayList<String> numbers = new ArrayList<String>();
// 2~10
for (int i = 2; i <= 10; i++) {
numbers.add(i + "");
}
// J Q K A
numbers.add("J");
numbers.add("Q");
numbers.add("K");
numbers.add("A");
// 1.3 准备花色牌 ♣ ♦ ♠ ♥
ArrayList<String> colors = new ArrayList<String>();
colors.add("♣");
colors.add("♦");
colors.add("♠");
colors.add("♥");
// 2♣ 2♦ 2♠ 2♥ 3♣ 3♦ 3♠ 3♥ ... 小☺ 大☺ (54张牌) 数字 + 花色
// 1.4 遍历, 通过数字牌和花色牌, 拼接出 每一张真正的扑克牌.
for (String number : numbers) { // 第一层 : 每一趟循环都是一模一样的. 2 2 2 2
for (String color : colors) { // 第一层 : 每一堂循环都是不一样的. ♣ ♦ ♠ ♥
String poker = number + color;
// 需要将拼接完成的每一张扑克牌存储到 pokers 集合中
pokers.add(poker);
}
}
// 1.5 单独添加 `大小王`
pokers.add("小☺");
pokers.add("大☺");
// 2. 洗牌 Collections 集合工具类
// static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换。
Collections.shuffle(pokers); // 参数类型 : List list
// 3. 发牌
// Iterator, foreach 这两种方式遍历, 没有下标.
// 3.1 定义三个玩家和一个底牌
ArrayList<String> player1 = new ArrayList<String>();
ArrayList<String> player2 = new ArrayList<String>();
ArrayList<String> player3 = new ArrayList<String>();
ArrayList<String> base = new ArrayList<String>();
int length = pokers.size();
for (int i = 0; i < length; i++) { // 3 % 3 -> 0
// 取牌
String poker = pokers.get(i);
if (i >= length - 3) {
// 底牌
base.add(poker);
} else {
// 玩家
if (i % 3 == 0) {
// 玩家1
player1.add(poker);
} else if (i % 3 == 1) {
// 玩家2
player2.add(poker);
} else {
// 玩家3
player3.add(poker);
}
}
}
// 4. 看牌
System.out.println(player1);
System.out.println(player2);
System.out.println(player3);
System.out.println(base);
}
}
5.List接口的特有方法
public class ListDemo {
public static void main(String[] args) {
// List 特有方法介绍 :
// 特点1 : 有序 特点2: 可以重复
// 字符串 : 柳岩, 范冰冰, 李冰冰, 林心如
ArrayList<String> list = new ArrayList<String>();
list.add("柳岩");
list.add("范冰冰");
list.add("李冰冰");
list.add("林心如");
// 需求1 : 将 `刘亦菲` 添加到两个 `冰冰` 之间.
list.add(2, "刘亦菲");
// 需求2 : 移除第一个 `冰冰`
String remove = list.remove(1);
System.out.println(remove);
// 需求3 : 获取剩下的那个 `冰冰`
String lbb = list.get(2);
System.out.println(lbb);
// 需求4 : 将 `林心如` 替换为 `汤唯`
list.set(3, "汤唯");
// 需求5 : 获取柳岩的 `下标`
int index = list.indexOf("柳岩");
System.out.println(index);
// 需求6 : 截取一个子集合 substring sublist
List<String> subList = list.subList(1, 3);
System.out.println(subList);
System.out.println(list);
}
}
6.使用 List 存储自定义对象,并去除重复元素
代码实现思路 :
代码实现 :
public class ListDuplicateDemo2 {
public static void main(String[] args) {
// 使用 List 存储自定义对象,并去除重复元素
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("柳岩", 18));
list.add(new Student("范冰冰", 18));
list.add(new Student("李冰冰", 18));
list.add(new Student("汤唯", 18));
list.add(new Student("李冰冰", 18));
list.add(new Student("范冰冰", 18));
// 1. 创建一个空集合, 存储list 集合中不重复的元素与
ArrayList<Student> list2 = new ArrayList<Student>();
// 2. 遍历 / 迭代 list 集合, 取出每一个元素, 在 list2 集合中判断是否存在
for (Student stu : list) {
// 失败的原因 : 出现在了 ArrayList 对 contain 方法的实现中. Student 类没有重写 equals 方法.
// 解决方案 : Student 类需要重写 Object 类的 equals 方法. contains 方法底层依赖与 对象的 equals 方法.
if (list2.contains(stu) == false) {
// 添加
list2.add(stu);
}
}
// 3. 清空 list 集合
list.clear();
// 4. 将 list2 中的不重复元素全部添加到 list 集合中
list.addAll(list2);
System.out.println(list);
}
}
重复对象产生的原因分析 :
解决方案 : 让Student 类重写 Object 类的 equals 方法.
// 重写 equals 方法
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
// obj 不是 Student 类型的对象
// return false;
throw new ClassCastException("类型错误!");
}
Student stu = (Student) obj;
// 比较标准 : 年龄和姓名一致, 就是同一个对象, 返回 true
if (this.name.equals(stu.name) && this.age == stu.age) {
return true;
}
return false;
}
测试输出结果 :
7.HashSet 存储字符串的实现原理分析
public class HashSetStringDemo {
public static void main(String[] args) {
/*
* set 接口的特点 :
* 特点1 : 存取无序
* 特点2 : 不可重复.
*/
// 需求 : 创建一个 HashSet 集合对象, 并添加四大天王
HashSet<String> set = new HashSet<String>();
set.add("刘德华");
set.add("张学友");
set.add("黎明");
set.add("郭富城");
// 迭代
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
}
}
分析图 :
8.HashSet 存储自定义对象保证不重复的实现原理分析
思考题 :
public class ThinkingQuestionDemo {
public static void main(String[] args) {
/*
* set 接口的特点 :
* 特点1 : 存取无序, 正确.
* 特点2 : 不可重复. 为什么出现重复的元素 ???
*/
// 需求 : 创建一个 HashSet 集合对象, 并添加四大天王
HashSet<Student> set = new HashSet<Student>();
set.add(new Student("刘德华", 18));
set.add(new Student("张学友", 18));
set.add(new Student("黎明", 18));
set.add(new Student("郭富城", 18));
set.add(new Student("刘德华", 18));
set.add(new Student("张学友", 18));
set.add(new Student("黎明", 18));
// 迭代
Iterator<Student> it = set.iterator();
while (it.hasNext()) {
Student stu = it.next();
System.out.println(stu);
}
}
}
Student 类的代码 :
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 重写 equals 方法
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
// obj 不是 Student 类型的对象
// return false;
throw new ClassCastException("类型错误!");
}
Student stu = (Student) obj;
// 比较标准 : 年龄和姓名一致, 就是同一个对象, 返回 true
if (this.name.equals(stu.name) && this.age == stu.age) {
return true;
}
return false;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
测试结果如下 : 为什么在 set 集合中出现了相同的重复元素 ???
说明 : String类可以排除重复元素的原因是该类不仅重写了equals方法, 同时也重写了 hashCode 方法, 根据字符串的内容来计算哈希值, 如果字符串内容相同, 那么计算出来的哈希值就相等, 如果字符串内容不相等, 那么计算出来的哈希值就不相同, 由于我们自定义类 Student 没有重写hashCode方法, 因此该方法会调用到 Object类的hashCode, 系统说明, 该方法是通过对象的内存地址计算的整型, 因此, new 的不同对象, 内存地址不一致, 就导致了 hashCode 值的不相同, 很大程度上就已经决定了对象的存储下标不同, 从而没有机会调用自定义类重写的equals方法了.
public class HashCodeDemo {
public static void main(String[] args) {
/*
* 20977295 % 10 -> 5
*/
// String 类实现了重写 Object 类的 hashCode 方法, 根据字符串的内容来计算哈希值, 如果字符串相同, 哈希值就会相同.
int hashCode1 = “刘德华”.hashCode();
System.out.println(hashCode1);
int hashCode2 = “刘德华”.hashCode();
System.out.println(hashCode2);
String s1 = new String("刘德华");
int hashCode3 = s1.hashCode();
System.out.println(hashCode3);
String s2 = new String("刘德华");
int hashCode4 = s2.hashCode();
System.out.println(hashCode4);
/*
* 49081624 % 10 -> 4
* 281134120 % 10 -> 0
*
*
*
* 29373287 % 10 -> 7
* 93287377 % 10 -> 7
*
* 说明 : 如果没有重写 Object 类的 hashCode 方法, 此时, 调用时就会执行 Object 类的该方法.
* 该方法底层是通过对象的内存地址进行运算, 最后将其转换为一个整型返回.
* 由于 new 的每一个对象内存地址都不一样, 因此返回的整型值也是都不一样.
*/
Student stu1 = new Student("刘德华", 18);
int hashCode5 = stu1.hashCode();
System.out.println(hashCode5);
Student stu2 = new Student("刘德华", 18);
int hashCode6 = stu2.hashCode();
System.out.println(hashCode6);
}
}
解决方案 : 重写 Student 类的 hashCode 方法.
// 重写 hashCode 方法
@Override
public int hashCode() {
// 根据姓名和年龄来计算
// name 是 String 类型的, 请问 : String 有没有重写 hashCode 方法 ? 有
return age + name.hashCode(); // 18 + 20977295 => 20977313
}
测试类中的代码与总结 :
public class ThinkingQuestionDemo {
public static void main(String[] args) {
/*
* set 接口的特点 :
* 特点1 : 存取无序, 正确.
* 特点2 : 不可重复. 为什么出现重复的元素 ???
*
* 需求 : 希望哈希值根据自定义对象的 `姓名+年龄` 运算, 如果姓名和年龄相同, 就应该计算出相同的哈希值.
* 解决方案 : Student 类重写 Object 类的 hashCode, 实现根据姓名和年龄进行运算哈希值.
*
* 总结 : 只要往 `哈希表` 中存储自定义对象,那么自定义对象的该类必须要重写 `hashCode + equals` 方法.
* 哈希表是如何保证元素唯一的 ??? 哈希表是通过 `hashCode+equals` 共同来保证元素唯一的.
*/
// 需求 : 创建一个 HashSet 集合对象, 并添加四大天王
HashSet<Student> set = new HashSet<Student>();
set.add(new Student("刘德华", 18));
set.add(new Student("张学友", 18));
set.add(new Student("黎明", 18));
set.add(new Student("郭富城", 18));
set.add(new Student("刘德华", 18));
set.add(new Student("张学友", 18));
set.add(new Student("黎明", 18));
// 迭代
Iterator<Student> it = set.iterator();
while (it.hasNext()) {
Student stu = it.next();
System.out.println(stu);
}
}
}
输出结果如下 :
9.LinkedList 链表集合
public class LinkedListDemo {
public static void main(String[] args) {
// LinkedList 链接链表 -> 链表
// LinkedList 关于头尾的方法设计演示 :
// 存储自定义的 Student 对象. `范冰冰, 李冰冰, 汤唯, 刘亦菲`
LinkedList<Student> list = new LinkedList<Student>();
list.add(new Student("范冰冰", 18));
list.add(new Student("李冰冰", 17));
list.add(new Student("汤唯", 20));
list.add(new Student("刘亦菲", 16));
// LinkedList 中的关于 `头与尾` 设计的方法
// 需求1 : 将 `柳岩` 添加到列表的开头, 并将`雷锋`添加到列表的结尾
list.addFirst(new Student("柳岩", 15));
list.addLast(new Student("雷锋", 22));
// 需求2 : 获取列表中的第一个元素和最后一个元素
Student first = list.getFirst();
Student last = list.getLast();
System.out.println(first);
System.out.println(last);
// 需求3 : 删除列表中的第一个元素与最后一个元素
Student removeFirst = list.removeFirst();
Student removeLast = list.removeLast();
System.out.println(removeFirst);
System.out.println(removeLast);
System.out.println(list);
}
}
演示一 : 带一个参数的泛型类定义.
// 自定义一个泛型类
// E -> Element T -> Type K -> Key V -> Value
// 问题 : 一个类上为什么要定义泛型呢 ??? 目的: 为了在类的内部使用.
class GenericClass {
// 属性
private T field;
// 行为
public void setField(T field) {
this.field = field;
}
public T getField() {
return field;
}
}
演示二 : 带两个参数的泛型类定义.
// 自定义一个带两个泛型的类
class GenericClass2
singable=cn.itcast.eveningparty.ZhangXueYou
dancable=cn.itcast.eveningparty.GirlsGeneration
performable=cn.itcast.eveningparty.ZhaoBenShan
singable=cn.itcast.eveningparty.LiuDeHua
dancable=cn.itcast.eveningparty.GirlsTeam
performable=cn.itcast.eveningparty.LiuQian
接口与实现类定义如下 : (Singable接口)
public interface Singable {
void sing(); // public abstract
}
public class ZhangXueYou implements Singable {
@Override
public void sing() {
System.out.println("张学友 演唱 `一路上有你`!");
}
}
public class LiuDeHua implements Singable {
@Override
public void sing() {
System.out.println("刘德华 演唱 `爱你一万年`!");
}
}
接口与实现类定义如下 : (Dancable接口)
public interface Dancable {
void dance();
}
public class GirlsGeneration implements Dancable {
@Override
public void dance() {
System.out.println("少女时代 跳 `Gee Gee Gee`!");
}
}
public class GirlsTeam implements Dancable {
@Override
public void dance() {
System.out.println("少女团伙 跳 `性感广场舞`!");
}
}
接口与实现类定义如下 : (Performable接口)
public interface Performable {
void perform();
}
public class ZhaoBenShan implements Performable {
@Override
public void perform() {
System.out.println("赵本山 表演 `卖拐`!");
}
}
public class LiuQian implements Performable {
@Override
public void perform() {
System.out.println("刘谦 表演 `大变死人`!");
}
}
执行结果 :
3.Computer 运行案例升级
Computer 类设计 :
public class Computer {
// 属性
// 行为
public void run() {
System.out.println("Computer run ... ");
}
// 外围设备 : 接口
public void useUSB(USB usb) {
if (usb != null) {
usb.open();
usb.close();
}
}
}
USB 接口设计 :
public interface USB {
void open();
void close();
}
配置文件 :
测试类代码编写 :
public class ComputerDemo {
public static void main(String[] args) throws Exception {
// XML 配置文件 :
// 加载文件
Properties prop = new Properties();
prop.load(new FileReader("usb.properties"));
// 电脑运行
Computer c = new Computer();
c.run();
for (int i = 1; i <= prop.size(); i++) {
// 需求 : 让外界设备鼠标运行
String className = prop.getProperty("usb" + i);
// 根据 className 字符串获取该字符串的 Class 对象
Class<?> cls = Class.forName(className);
// 根据 cls 对象调用 newInstance() 方法, 创建该 cls 对象表示的真实对象
USB usb = (USB) cls.newInstance();
c.useUSB(usb);
}
/*
Mouse m = new Mouse();
c.useUSB(m);
Keyboard k = new Keyboard();
c.useUSB(k);
CameraVideo cv = new CameraVideo();
c.useUSB(cv);
*/
}
}
Mouse 接口实现类 :
public class Mouse implements USB {
@Override
public void open() {
System.out.println("Mouse open ...");
}
@Override
public void close() {
System.out.println("Mouse close ...");
}
}
Keyboard 接口实现类 :
public class Keyboard implements USB {
@Override
public void open() {
System.out.println("Keyboard open ...");
}
@Override
public void close() {
System.out.println("Keyboard close ...");
}
}
CameraVideo 接口实现类 :
public class CameraVideo implements USB {
@Override
public void open() {
System.out.println("CameraVideo open ...");
}
@Override
public void close() {
System.out.println("CameraVideo close ...");
}
}
实现输出效果 :
自定义注解 :
使用注解 :
解析注解 :
public class AnnotationDemo {
@Test
public void demo01() throws Exception {
// 解析注解 : AnnotatedElement 接口
// 1. isAnnotationPresent 是否存在注解信息
// 2. getAnnotation 获取 `注解对象` 注解对象里面封装了很多的 `注解信息`
// 1. 注解是使用反射机制获取的. 反射第一步是获取 Class 对象
Class<?> cls = Class.forName("cn.itcast.annotation.Student");
// 2. 使用 cls 对象获取 `方法对象`
Method method = cls.getMethod("introduce", new Class[]{});
// 3. 判断 method 对象上是否存在注解信息
if (method.isAnnotationPresent(Description.class)) {
// 条件成立, 说明该方法对象上存在 Description 注解信息
Description annotation = method.getAnnotation(Description.class);
// 从注解对象中, 获取注解信息
String desc = annotation.desc();
String author = annotation.author();
int age = annotation.age();
System.out.println(desc + " : " + author + " : " + age);
}
System.out.println("-------------");
}
}
2.自定义@Test实现
自定义注解类 :
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
自定义类使用注解 :
public class MyTestClass {
@MyTest
public void demo01() {
System.out.println("测试方法一被执行...");
}
@MyTest
public void demo02() {
System.out.println("测试方法二被执行...");
}
// @MyTest
public void demo03() {
System.out.println("测试方法三被执行...");
}
}
模拟测试类 :
public class MyTestDemo {
public static void main(String[] args) throws Exception {
// 模拟实现 : @Before @After
// 1. 反射执行类的 Class 对象
Class<?> cls = Class.forName("cn.itcast.mytest.MyTestClass");
Object obj = cls.newInstance();
// 2. 使用 cls 对象获取该类的所有方法
Method[] methods = cls.getMethods();
// 3. 遍历方法对象数组
for (Method method : methods) {
// 判断 `method` 对象上是否存在指定注解 MyTest
if (method.isAnnotationPresent(MyTest.class)) {
// 如果有, 执行该方法
// method.invoke(obj, new Object[]{}); // 没有参数
method.invoke(obj);
}
}
}
}
接口的定义 :
public interface SuperStar {
void sing(int money);
void liveShow(int money);
void sleep();
}
实现类代码 :
public class LiuYan implements SuperStar {
@Override
public void sing(int money) {
System.out.println("演唱了一首 <<真的真的很爱你>> 歌曲!");
System.out.println("挣了 : " + money);
}
@Override
public void liveShow(int money) {
System.out.println("参加了 <<Running Man>> 节目!");
System.out.println("挣了 : " + money);
}
@Override
public void sleep() {
System.out.println("真的真的很累了, 休息一下~~~~");
}
}
动态代理实现代码 :
/*
* 代理 : 中介
*
* 动态代理 : 对被代理对象的 所有方法 (定义在接口中)
进行拦截和控制.
*
* 特点 : 代理对象和被代理对象应该具体相同的行为规范. (接口)
*
* 动态代理的前提条件 : 必须有接口.
*
* 经纪人 : 柳岩 (说明柳岩真实对象的哪些方法可以被经纪人代理)
*/
public class ArithmeticDemo1 {
@Test
public void test1() {
// 1. 创建一个真实对象
LiuYan ly = new LiuYan();
// 2. 直接调用真实对象的方法
ly.sing(10);
ly.liveShow(20);
ly.sleep();
}
@Test
public void test2() {
// 1. 创建一个真实对象
// 如果在动态代理内部, 使用到外部对象, 那么外部对象必须使用 final 修饰. JDK 8.0 之后, final 可以不用书写, 自动生成.
final LiuYan ly = new LiuYan();
// 2. 找到代理, proxy
/************** 动态创建一个代理对象 start ****************/
/*
* Proxy 类的作用 : 在程序运行阶段, 动态创建一个代理对象.
* 1. ClassLoader loader 类加载器.
* 方式一 : 类名.class.getClassLoader();
* 方法二 : 对象名.getClass().getClassLoader();
* 2. Class<?>[] interfaces Class数组类型. 接口 new Class[]{SuperStar.class};
* 方式 : ly.getClass().getInterfaces();
* 3. InvocationHandler h 调用处理器是一个接口, 第三个参数是一个接口类型的参数.
* 如果一个方法的参数是一个接口类型, 其真正需要的是该接口的实现类对象.
* 方式一 : new InvocationHandler(); 错误! 接口没有构造方法, 不能实例化对象.
* 方式二 : new InvocationHandler(){}; 正确! 实例化该接口的实现类对象.
*/
// ClassLoader loader = ArithmeticDemo1.class.getClassLoader();
ClassLoader loader = ly.getClass().getClassLoader();
Class<?>[] interfaces = ly.getClass().getInterfaces();
// 动态代理返回的对象必须使用对应的接口类型类接收.
SuperStar proxy = (SuperStar) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler(){
/*
* 1. Object proxy 代理对象.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 说明 : 在这里就可以对 `柳岩` 的所有方法进行拦截和控制
// 1. 获取方法的名称
String methodName = method.getName();
// 2. 判断
if ("sing".equals(methodName)) {
// 唱歌方法
int money = (int) args[0];
// 判断金额
if (money < 10000) {
System.out.println("滚, 你这个穷屌丝, 回家撸代码去...");
return null;
}
// 钱够
System.out.println("代理抽取了 : " + (money * 0.3) + " 元.");
// 调用柳岩的唱歌方法
return method.invoke(ly, (int) (money * 0.7));
} else if ("liveShow".equals(methodName)) {
// 真人秀方法
int money = (int) args[0];
// 判断金额
if (money < 100000) {
System.out.println("滚, 怎么还是你这个穷屌丝, 继续回家撸代码去...");
return null;
}
// 钱够
System.out.println("代理抽取了 : " + (money * 0.3) + " 元.");
// 调用柳岩的真人秀方法
return method.invoke(ly, (int) (money * 0.7));
}
// 其它情况, 都正常执行. 不处理...
return method.invoke(ly, args);
}
});
/************** 动态创建一个代理对象 end ****************/
// 3. 使用代理来调用对象的方法
// 记住 : 只要调用代理的方法, 代理在底层就会将该方法转交给 invoke 执行.
proxy.sing(10000);
proxy.liveShow(200000);
proxy.sleep();
}
}
1.如何去写一个XML
store.xml
相关文章
- 暂无相关文章
用户点评