文章目录
示例代码下载: CongSec
java的运行机制及特征
-
特征
-
Java语言是面向对象的(oop)
-
Java语言是健壮的。 Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证
-
Java语言是跨平台性的。 [即:一个编译好的.class文件可以在多个系统下运行,这种特性称为跨平台]
-
Java 语言是解释型
- 解释性语言:javascript,PHP,java
- 编译性语言:c/c++
- 区别是:解释性语言,编译后的代码,不能直接被机器执行,需要解释器来执行,编译性语言,编译后的代码,可以直接被机器执行,c/c++
-
-
运行机制
什么是JDK,JRE
-
JDK
- JDK也称java开发的工具包,JDK=JRE+java的开发工具[java,javac,javadoc.javap等],比如说
javac hello.java
这个就是产生Java虚拟机可以识别的字节码文件 - JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。
- JDK也称java开发的工具包,JDK=JRE+java的开发工具[java,javac,javadoc.javap等],比如说
-
JRE
- JRE 也称Java 运行环境 JRE=JVM+Java的核心类库[类],比如说
java hello
这个命令就相当于启动java虚拟机的意思 - 包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序, 计算机中只需要安装JRE即可。
- JRE 也称Java 运行环境 JRE=JVM+Java的核心类库[类],比如说
-
JDK、JRE和JVM的包含关系
- JDK=JRE+开发工具集(例如Javac.java编译工具等)
- JRE=JVM+JavaSE标准类库(java核心类库)
- 如果只想运行开发好的.class文件只需要JRE
快速入门
-
代码示例
-
public class hello { public static void main(String[] args) { System.out.println("hello,world!"); } } class Dog { public static void main(String[] args) { System.out.println("hello,小狗狗~"); } } class Tiger { public static void main(String[] args) { System.out.println("hello,小老虎~"); } }
-
-
原理解释
-
使用
javac hello.java
命令进行编译可以看到当前目录下会生成三个文件,分别是程序的三个类文件,这三个类文件都是可以运行的
-
-
注意事项
-
1.Java源文件以.java 为扩展名。源文件的基本组成部分是类(class),如本类中的Hello
2. Java应用程序的执行入口是main()方法。它有固定的书写格式:
public static void main(String[] args) {…}
3.Java语言严格区分大小写。
4. Java方法由一条条语句构成,每个语句以“;”结束。
5.大括号都是成对出现的,缺一不可。[习惯,先写{}再写代码]
6.一个源文件中最多只能有一个public类。其它类的个数不限。
7.如果源文件包含一个public类,则文件名必须按该类名命名!
8. 一个源文件中最多只能有一个public类。其它类的个数不限,也可以将main方法写在非public类中,然后指定运行非public 类,这样入口方法就是非public 的main方法
9.java数值默认是int,double,string,使用float是后面数字要添加f,使用long是添加l,单字符用单引号
10.一个英文字符占一个字节,在gdk中,一个中文名占两个字节,在utf8中占3个字节
11.当一个精度高的转化为低的时候会报错
-
转义字符
-
\t
制表位 -
\n
换行符 -
\r
回车
注释类型
-
单行注释
//这里是注释的单行文字
-
多行注释
/* 这是一个多行文字 */
-
文档注释
/** 这是一个用来演示文档注释的类 @author 某某 @version 1.0 */
变量以及数据类型
-
数据类型
-
-
定义
-
整数型
byte
: 1字节 (8位,有符号)short
: 2字节 (16位,有符号)int
: 4字节 (32位,有符号)long
: 8字节 (64位,有符号)
-
浮点数型
float
: 4字节 (32位,IEEE 754标准)double
: 8字节 (64位,IEEE 754标准)
-
字符型:
char
: 2字节(16位,用来表示Unicode字符) -
**字符串型:**
String
-
**布尔型:**
boolean
-
-
代码示例
-
public class hello{ public static void main(String[] args){ int age=30; double score=88.9; char gender='男'; String name="king"; System.out.println("人的信息如下:"); System.out.println(name); System.out.println(age); System.out.println(score); System.out.println(gender); } }
-
-
-
自动类型转换
-
强制类型转换
常用编码
-
ASCII 码
- 上个世纪60年代,美国制定了一套字符编码(使用一个字节),对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码。ASCII码一共规定了128个字符的编码,只占用了一个字节的后面7位,最前面的1位统一规定为0。特别提示:一个字节可以表示256个字符,ASCII码只用了128个字符.
- 看一个完整的ASCII码表[资料中]
- 缺点:不能表示所有字符。
-
Unicode 编码
- Unicode的好处:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独
一无二的编码,使用 Unicode 没有乱码的问题。 - Unicode 的缺点:一个英文字母和一个汉字都占用2个字节,这对于存储空间来说是浪费。
3.2的16次方是 65536,所以最多编码是65536个字符。 - 编码0—127的字符是与ASCII的编码一样·比如 ‘a’ 在ASCII码是 Ox61, 在 unicode码是
ox0061, 都对应97. 因此 Unicode码兼容 ASCII码.
- Unicode的好处:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独
-
UTF-8 编码
- UTF—8是在互联网上使用最广的一种Unicode的实现方式(改进)
- UTF—8是一种变长的编码方式。它可以使用1—6个字节表示一个符号,根据不
同的符号而变化字节长度。 - 使用大小可变的编码字母占1个字节,汉字占3个字节
运算符
算术运算符
操作符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 正号 | +7 | 7 |
- | 负号 | b=11; -b | -11 |
+ | 加 | 9+9 | 18 |
- | 减 | 10-8 | 2 |
* | 乘 | 7*8 | 56 |
/ | 除 | 9/9 | 1 |
% | 取模(取余数) | 11%9 | 2 |
++ | 自增(前):先增加再返回值 | a=2;b=++a; | a=3;b=3 |
++ | 自增(后):先返回值再增加 | a=2;b=a++; | a=3;b=2 |
– | 自减(前):先减少再返回值 | a=2;b=–a; | a=1;b=1 |
– | 自减(后):先返回值再减少 | a=2;b=a–; | a=1;b=2 |
+ | 字符串拼接 | “hsp”+“edu” | “hspedu” |
//i++与++i的区别
public class hello{
public static void main(String[] args){
int i = 1;
i = i++;
System.out.println(i); // 1
int a = 1;
a = ++a;
System.out.println(a);//2
}
}
关系运算符
运算符 | 描述 | 实例 | 结果 |
---|---|---|---|
== | 相等 | 8==7 | false |
!= | 不等 | 8!=7 | true |
< | 小于 | 8<7 | false |
> | 大于 | 8>7 | true |
<= | 小于等于 | 8<=7 | false |
>= | 大于等于 | 8>=7 | true |
instanceof | 检查是否是类的实例 | "hsp" instanceof String | true |
逻辑运算符
a | b | a&b | a&&b | a|b | a||b | !a | a^b |
---|---|---|---|---|---|---|---|
true | true | true | true | true | true | false | false |
true | false | false | false | true | true | false | true |
false | true | false | false | true | true | true | true |
false | false | false | false | false | false | true | false |
-
短路与
&&
- 这是一个条件运算符,仅当第一个操作数的结果为
true
时才会评估第二个操作数。如果第一个操作数为false
,那么不论第二个操作数的值是什么,整个表达式的结果都是false
,且第二个操作数不会被评估。这种行为称为“短路”,因为它可以阻止不必要的计算
- 这是一个条件运算符,仅当第一个操作数的结果为
-
短路或
||
- 同样是一个条件运算符,仅当第一个操作数的结果为
false
时才会评估第二个操作数。如果第一个操作数为true
,则整个表达式的结果立即确定为true
,并且第二个操作数不会被评估
- 同样是一个条件运算符,仅当第一个操作数的结果为
-
取反
!
- 这是一个布尔逻辑运算符,用于反转其单个操作数的布尔值。如果操作数为
true
,则结果为false
,反之亦然
- 这是一个布尔逻辑运算符,用于反转其单个操作数的布尔值。如果操作数为
-
逻辑与
&
- 作为布尔逻辑运算符时,它需要评估两个操作数,无论第一个操作数的值如何。当用作位运算符时,它执行按位与操作,即对两个操作数的每一位进行与运算
-
逻辑或
|
- 作为布尔逻辑运算符时,它同样需要评估两个操作数,无论第一个操作数的值如何。作为位运算符时,它执行按位或操作,即对两个操作数的每一位进行或运算
-
逻辑异或
^
- 作为布尔逻辑运算符,如果两个操作数具有不同的值,则结果为
true
;如果两个操作数相同,则结果为false
。作为位运算符,它执行按位异或操作,即对两个操作数的每一位进行异或运算。如果位相同,则结果位为0
;如果位不同,则结果位为1
- 作为布尔逻辑运算符,如果两个操作数具有不同的值,则结果为
三元运算符
运算符的优先级
方向 | 操作符 |
---|---|
. ,() ,{} ,, | |
R→L | ++ , -- , ~ , !(data type) |
L→R | * , / , % |
L→R | + , - |
L→R | << , >> , >>> , 位 |
L→R | < , > , <= , >= , instanceof |
L→R | == , != |
L→R | & |
L→R | ^ |
L→R | | |
L→R | && |
L→R | || |
L→R | ? , : |
R→L | = , *= , /= , %= |
+= , -= , <<= , >>= | |
>>>= , &= , ^= , |= |
JAVA包
-
java.util包
-
键盘输入–Scanner类
-
介绍
-
使用
-
导入,
import java.util.Scanner;
-
代码示例
-
import java.util.Scanner; public class hello{ public static void main(String[] args){ Scanner myscanner=new Scanner(System.in); System.out.println("请输入名字"); String name=myscanner.next(); System.out.println("请输入年龄"); int age=myscanner.nextInt(); System.out.println("请输入薪水"); double sal=myscanner.nextDouble(); System.out.println("人的信息如下:"); System.out.println("名字="+ name + " 年龄="+ age +" 薪水=" + sal); } }
-
-
-
-
进制转换
-
介绍
- 二进制:0,1,满2进1.以0b或0B开头。
- 十进制:0—9,满10进1。
- 八进制:0—7,满8进1.以数字0开头表示。
- 十六进制:0—9及A(10)—F(15),满16进1.以0x或0X开头表示。此处的A—F不区分大小写。
-
十进制 十六进制 八进制 二进制 0 0 0 0 1 1 1 1 2 2 2 10 3 3 3 11 4 4 4 100 5 5 5 101 6 6 6 110 7 7 7 111 8 8 10 1000 9 9 11 1001 10 A 12 1010 11 B 13 1011 12 C 14 1100 13 D 15 1101 14 E 16 1110 15 F 17 1111 16 10 20 10000 17 11 21 10001
-
代码示例
-
public class hello { public static void main(String[] args) { // 二进制表示 int n1 = 0b1010; // 表示十进制的 10 // 十进制表示 int n2 = 1010; // 直接写的是十进制数 // 八进制表示 int n3 = 01010; // 表示十进制的 520 // 十六进制表示 int n4 = 0X10101; // 表示十进制的 65793 System.out.println("n1=" + n1);//10 System.out.println("n2=" + n2);//1010 System.out.println("n3=" + n3);//520 System.out.println("n4=" + n4);//65793 // 打印十六进制数0x23A的十进制值 System.out.println(0x23A); // 表示十进制的 570 } }
-
-
进制转换
程序控制结构
-
顺序控制
- 程序从上到下逐行地执行,中间没有任何判断和跳转。
-
分支控制
-
嵌套分支
-
介绍
- 在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分支外面的分支结构称为外层分支。老师建议:不要超过3层(可读性不好)
-
代码示例
-
import java.util.Scanner; public class hello { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); System.out.println("请输入该歌手的成绩:"); double score = myScanner.nextDouble(); if (score > 8.0) { System.out.println("请输入性别 (男/女):"); char gender = myScanner.next().charAt(0); if (gender == '男') { System.out.println("进入男子组"); } else if (gender == '女') { System.out.println("进入女子组"); } else { System.out.println("你的性别有误,不能参加决赛"); } } else { System.out.println("Sorry, 你被淘汰了"); } myScanner.close(); } }
-
-
-
switch分支结构
-
-
基本语法
-
switch (表达式) { case 常量1: // 语句块1 break; case 常量2: // 语句块2 break; case 常量n: // 语句块n break; default: // default语句块 break; }
-
-
代码示例
-
//题目:请编写一个程序,该程序可以接收一个字符,比如:a,b,c,d,e,f,ga表示星期一,b表示星期二 . //根据用户的输入显示相应的信息.要求使用 switch 语句完成 import java.util.Scanner; public class hello { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); System.out.println("请输入一个字符(a-g):"); char c1 = myScanner.next().charAt(0); switch (c1) { case 'a': System.out.println("今天星期一,猴子穿新衣"); break; case 'b': System.out.println("今天星期二,猴子当小二"); break; case 'c': System.out.println("今天星期三,猴子爬雪山."); break; default: System.out.println("你输入的字符不正确,没有匹配的"); System.out.println("退出了switch,继续执行程序"); } myScanner.close(); } }
-
-
注意事项
-
表达式数据类型,应和case后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,而常量是int
-
switch(表达式)中表达式的返回值必须是:(byte,short,int,char,enum[枚举],String)
-
double c= 1.1; switch(c){//错误 case 1.1://错误 System.out. println("ok3"); break; }
-
-
case子句中的值必须是常量,而不能是变量
-
default子句是可选的,当没有匹配的case时,执行default
-
break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有写break,程序会顺序执行到switch结尾,除非遇到break;
-
-
-
for循环结构
-
基本语法
-
for (循环变量初始化;循环条件;循环变量迭代) { 循环操作(可以多条语句); }
-
-
注意事项
- 循环条件是返回一个布尔值的表达式
- for(;循环判断条件;) 中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略。
- 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开。
-
代码示例
-
public class hello { public static void main(String[] args) { for (int i = 1; i <= 10; i++) { System.out.println("你好,cong" + i); } } }
-
-
-
while循环控制
-
do…while语法控制
-
多重循环
-
介绍
- 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do…while均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层,否则,代码的可读性很差】
- 实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环[听不懂,走案例]。
-
代码示例
-
打印九九乘法表
-
public class hello { public static void main(String[] args) { // 外层循环控制行(乘数) for (int i = 1; i <= 9; i++) { // 内层循环控制列(被乘数) for (int j = 1; j <= i; j++) { // 打印乘法表的一项,格式为:j * i = product System.out.print(j + " * " + i + " = " + (j * i) + "\t"); // 使用 \t 来添加制表符,使输出对齐 } // 每完成一行打印一个换行符,开始新的一行 System.out.println(); } } }
-
-
打印金字塔
-
public class hello { public static void main(String[] args) { //打印99乘法表 for(int x=1; x<=9; x=x+2){ if(x!=9) { for (int z = 1; z <= 9 - x; z = z + 2) { System.out.print(" "); } for (int y = 1; y <= x; y++) { if (y == 1 | y == x) { System.out.print("*"); } else { System.out.print(" "); } } System.out.println(); }else{ for(int i=1; i<=9; i++){ System.out.print("*"); } } } } } 输出结果: * * * * * * * *********
-
-
-
-
循环控制函数
-
break函数
-
介绍
- break语句用于终止某个语句块的执行,一般使用在switch或者循环[for,while,do-while]中
-
代码示例
-
随机数
-
public class hello { public static void main(String[] args) { int count = 0; // 初始化计数器 int number = 0; // 初始化随机数变量 // 无限循环,直到满足条件后用 break 退出 while (true) { number = (int) (Math.random() * 100) + 1; // 生成 1 到 100 之间的随机数 count++; // 每生成一次,计数器加一 System.out.println("Attempt " + count + ": " + number); if (number == 97) { break; // 如果生成的数字是 97,退出循环 } } // 输出最终结果 System.out.println("It took " + count + " attempts to generate the number 97."); } }
-
-
-
-
continue函数
-
介绍
- continue语句用于结束本次循环,继续执行下一次循环。
- continue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环,这个和前面的标签的使用的规则一样.
-
代码示例
-
public class hello { public static void main(String[] args) { int i = 1; // 初始化 i while (i <= 4) { i++; if (i == 2) { continue; // 当 i 等于 2 时,跳过本次循环的剩余部分 } System.out.println("i=" + i); // 打印 i 的值 } } } //i=3 //i=4 //i=5
-
-
-
return函数
-
介绍
- return使用在方法,表示跳出所在的方法,在讲解方法的时候,会详细的介绍,这里我们简单的提一下。注意:如果 return 写在 main方法,退出程序.
-
代码示例
-
public class hello { public static void main(String[] args) { for (int i = 1; i <= 5; i++) { if (i == 3) { // 使用双等号 == 进行比较 System.out.println("cong" + i); return; // 当 i 等于 3 时,退出 main 方法 } System.out.println("Hello World!"); } System.out.println("go on.."); // 这行代码将不会被执行,因为 main 方法在 i 等于 } } //Hello World! //Hello World! //cong3
-
-
-
数组,排序和查找
数组
-
介绍
- 数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型。即:数(数据)组(一组)就是一组数据
-
代码示例
-
public class hello { public static void main(String[] args) { double[] hens = {3, 5, 1, 3.4, 2, 50, 7.8, 88.8, 1.1 double totalWeight = 0; for (int i = 0; i < hens.length; i++) { totalWeight += hens[i]; } System.out.println("总体重=" + totalWeight + " 平均体重=" + } } //总体重=267.7 平均体重=24.336363636363636
-
要求:请求出一个数组int[]的最大值{4,-1,9,10,23},并得到对应的下标。 public class hello { // 编写一个main方法 public static void main(String[] args) { int[] arr = {4, -1, 9, 10, 23}; int max = arr[0]; // 假定第一个元素就是最大值 int maxIndex = 0; // 初始化最大值的索引 for (int i = 1; i < arr.length; i++) { // 从下标 1 开始遍历 arr if (max < arr[i]) { // 如果 max < 当前元素 max = arr[i]; // 把 max 设置成当前元素 maxIndex = i; // 更新最大值的索引 } } System.out.println("最大值=" + max + " 最大值的下标 =" + maxIndex); } } //最大值=23 最大值的下标 =4
-
-
数组的使用
-
注意事项
- 数组是多个相同类型数据的组合,实现对这些数据的统一管理
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
- 数组创建后,如果没有赋值,有默认值int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
- 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
- 数组的下标是从0开始的。
- 数组下标必须在指定范围内使用,否则报:下标越界异常,比如int [] arr=new int[5]; 则有效下标为 0-4
排序
-
介绍
- 排序是将多个数据,依指定的顺序进行排列的过程。
-
内部排序
- 指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法);
-
外部排序
-
介绍
- 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。
-
冒泡排序
-
介绍
- 冒泡排序(BubbleSorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素 的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
-
代码示例
-
要求:们将五个无序:24,69,80,57,13使用冒泡排序法将其排成一个从小到大的有序数列 public class hello { // 编写一个main方法 public static void main(String[] args) { int[] arr = {24,69,80,57,13}; int temp; // 用于辅助交换的变量 for (int i = 0; i < arr.length - 1; i++) { // 外层循环控制总的排序轮次 for (int j = 0; j < arr.length - 1 - i; j++) { // 内层循环进行相邻元素比较和交换 // 如果前面的数大于后面的数,则交换 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } // 打印每轮排序后的数组状态 System.out.println("\n==第" + (i + 1) + "轮=="); for (int j = 0; j < arr.length; j++) { System.out.print(arr[j] + "\t"); } } } } 输出结果: ==第1轮== 24 69 57 13 80 ==第2轮== 24 57 13 69 80 ==第3轮== 24 13 57 69 80 ==第4轮== 13 24 57 69 80
-
-
-
查找
-
顺序查找
-
代码示例
-
要求:有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王猜数游戏:从键盘中任意输入一个名称,判断数列中是否 包含此名称【顺序查找】要求:如果找到了,就提示找到,并给出下标值。 import java.util.Scanner; public class SeqSearch { // 编写一个main方法 public static void main(String[] args) { String[] names = {"白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"}; Scanner myScanner = new Scanner(System.in); System.out.println("请输入名字"); String findName = myScanner.next(); int index = -1; for (int i = 0; i < names.length; i++) { // 比较 字符串比较 equals, 如果要找到名字就是当前元素 if (findName.equals(names[i])) { System.out.println("恭喜你找到 " + findName); System.out.println("下标为= " + i); // 把 i 保存到 index index = i; break; // 退出循环 } } if (index == -1) { // 没有找到 System.out.println("sorry, 没有找到 " + findName); } myScanner.close(); // 关闭扫描器 } }
-
-
-
二分查找
- 此部分先跳过
二维数组
-
介绍
- 在Java中,二维数组是数组的数组,通常用于存储表格数据,例如矩阵。它可以看作是有多行多列的数组,每一行都是一个一维数组。
-
使用
-
动态初始化
-
基本语法
- 类型[][] 数组名=new 类型[大小][大小];
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { int[][] arr; // 声明二维数组 arr = new int[2][3]; // 再开空间 arr[1][1] = 8; // 初始化其中一个元素 // 遍历arr数组 for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { // 对每个一维数组遍历 System.out.print(arr[i][j] + " "); // 打印元素和一个空格 } System.out.println(); // 换行 } } } //0 0 0 //0 8 0
-
-
-
动态初始化
-
基本语法
- 类型 数组名[][];
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { int[][] arr = new int[3][]; for (int i = 0; i < arr.length; i++) { // 遍历 arr 的每个一维数组 arr[i] = new int[i + 1]; // 给每个一维数组开空间 new // 如果没有给一维数组 new, 那么 arr[i] 就是 null // 遍历一维数组,并给一维数组的每个元素赋值 for (int j = 0; j < arr[i].length; j++) { arr[i][j] = i + 1; // 赋值 } } // 遍历arr输出 for (int i = 0; i < arr.length; i++) { // 输出arr的每个一维数组 for (int j = 0; j < arr[i].length; j++) { System.out.print(arr[i][j] + " "); // 在输出数字后添加一个空格 } System.out.println(); // 换行 } } } //1 //2 2 //3 3 3
-
-
-
静态初始化
-
-
代码示例
-
public class TwoDimensionalArray01 { // 编写一个main方法 public static void main(String[] args) { int[][] arr = { {0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0}, {0, 2, 0, 3, 0, 0}, {0, 0, 0, 0, 0, 0} }; // 关于二维数组的关键概念 System.out.println("二维数组的元素个数=" + arr.length); // 举例访问第3个一维数组的第4个值,它是3 System.out.println("第3个一维数组的第4个值=" + arr[2][3]); // 输出3 // 输出二维图形 for (int i = 0; i < arr.length; i++) { // 遍历二维数组的每个元素 for (int j = 0; j < arr[i].length; j++) { // 遍历二维数组的每个元素(数组) System.out.print(arr[i][j] + " "); // 输出一维数组的元素,并添加空格 } System.out.println(); // 换行 } } } 输出结果: 二维数组的元素个数=4 第3个一维数组的第4个值=3 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0
-
杨辉三角
-
public class hello { // 编写一个main方法 public static void main(String[] args) { int[][] yangHui = new int[9][]; for (int i = 0; i < yangHui.length; i++) { // 遍历 yangHui 的每个元素 // 给每个一维数组(行) 开空间 yangHui[i] = new int[i + 1]; // 给每个一维数组(行) 赋值 for (int j = 0; j < yangHui[i].length; j++) { // 每一行的第一个元素和最后一个元素都是1 if (j == 0 || j == yangHui[i].length - 1) { yangHui[i][j] = 1; } else { // 中间的元素 yangHui[i][j] = yangHui[i - 1][j] + yangHui[i - 1][j - 1]; } } } // 输出杨辉三角 for (int i = 0; i < yangHui.length; i++) { for (int j = 0; j < yangHui[i].length; j++) { // 遍历输出该行 System.out.print(yangHui[i][j] + "\t"); } System.out.println(); // 换行. } } } 输出结果: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1
-
-
类与对象
-
介绍
- 在面向对象编程(OOP)中,类 是定义对象属性和行为的蓝图,它是一种抽象的数据类型,而对象 是根据类的模板创建的具体实例。类定义了一组属性(数据字段)和方法(函数),对象则是这些属性和方法的具体化,每个对象都独立拥有自己的属性值但共享同一类的方法。这种机制使得软件开发更加模块化,易于管理和维护。
-
区别和联系
- 类是抽象的,概念的,代表一类事物,比如人类,猫类…, 即它是数据类型.
- 对象是具体的,实际的,代表一个具体事物, 即 是实例.
- 类是对象的模板,对象是类的一个个体,对应一个实例
-
创建对象
-
先声明再创建
- Cat cat ; //声明对象 cat
- cat = new Cat(); //创建
-
直接创建
- Cat cat = new Cat();
-
-
克隆对象
-
代码示例
-
public class hello { // 编写一个 main 方法 public static void main(String[] args) { // 创建第一只猫对象 cat1 Cat cat1 = new Cat(); cat1.name = "小白"; cat1.age = 3; cat1.color = "白色"; cat1.weight = 10; // 创建第二只猫对象 cat2 Cat cat2 = new Cat(); cat2.name = "小花"; cat2.age = 100; cat2.color = "花色"; cat2.weight = 20; // 输出第一只猫的信息 System.out.println("第 1 只猫信息: " + cat1.name + " " + cat1.age + " " + cat1.color + " " + cat1.weight); // 输出第二只猫的信息 System.out.println("第 2 只猫信息: " + cat2.name + " " + cat2.age + " " + cat2.color + " " + cat2.weight); } } // 定义猫类 Cat -> 自定义的数据类型 class Cat { // 属性/成员变量 String name; // 名字 int age; // 年龄 String color; // 颜色 double weight; // 体重 // 行为可以通过定义方法来表示,如 miaow() 方法等,目前暂未定义行为 } //第 1 只猫信息: 小白 3 白色 10.0 //第 2 只猫信息: 小花 100 花色 20.0
-
-
-
属性
-
介绍
- 成员变量 = 属性 =field(字段) (即 成员变量是用来表示属性的,授课中,统一叫 属性)
- 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。比如我们前面定义猫类的int age就 是属性
-
注意事项
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
- 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一致。具体说:int 0,shor t0,byte 0,lon g0,float 0.0,double 0.0,char \u0000, boolean false,String null
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { // 创建Person 对象 // p1 是对象名(对象引用) // new Person() 创建的对象空间(数据) 才是真正的对象 Person p1 = new Person(); // 对象的属性默认值,遵守数组规则: // int 0,short 0, byte 0, long 0, float 0.0, double 0.0,char \u0000,boolean false,String null System.out.println("\n当前这个人的信息"); System.out.println("age=" + p1.age + " name=" + p1.name + " sal=" + p1.sal + " isPass=" + p1.isPass); } } class Person { // 四个属性 int age; String name; double sal; boolean isPass; } 输出结果: 当前这个人的信息 age=0 name=null sal=0.0 isPass=false
-
-
-
内存分配机制
-
代码
-
Person p1=new Person(); p1.age=10; p1.name="小明"; Person p2=p1;//把p1赋给了p2,让p2指向p1 System.out.println(p2.age);
-
-
机制原理图
-
-
方法
-
介绍
- 在某些情况下,我们要需要定义成员方法(简称方法)。比如人类:除了有一些属性外(年龄,姓名.),我们人类还有一些行为比如:可以说话、跑步.,通过学习,还可以做算术题。这时就要用成员方法才能完成。现在要求对Person类完善。
-
基本使用
-
public class hello { // 编写一个main方法 public static void main(String[] args) { // 方法使用 // 1.方法写好后,如果不去调用(使用),不会输出 // 2.先创建对象,然后调用方法即可 Person p1 = new Person(); p1.speak(); // 调用speak方法 p1.cal01(); // 调用cal01方法 p1.cal02(5); // 调用cal02方法,同时给n=5 p1.cal02(10); // 调用cal02方法,同时给n=10 // 调用getSum方法,同时num1=10, num2=20 // 把方法getSum返回的值,赋给变量returnRes int returnRes = p1.getSum(10, 20); System.out.println("getSum 方法返回的值 = " + returnRes); } } class Person { String name; int age; // 方法(成员方法) // 添加speak成员方法,输出“我是一个好人” public void speak() { System.out.println("我是一个好人"); } // 添加cal01成员方法,可以计算从1+..+1000的结果 public void cal01() { int res = 0; for (int i = 1; i <= 1000; i++) { res += i; } System.out.println("cal01 方法计算结果 = " + res); } // 添加cal02成员方法,该方法可以接收一个数n,计算从1+..+n的结果 public void cal02(int n) { int res = 0; for (int i = 1; i <= n; i++) { res += i; } System.out.println("cal02 方法计算结果 = " + res); } // 添加getSum成员方法,可以计算两个数的和 public int getSum(int num1, int num2) { int res = num1 + num2; return res; } } 输出结果: 我是一个好人 cal01 方法计算结果 = 500500 cal02 方法计算结果 = 15 cal02 方法计算结果 = 55 getSum 方法返回的值 = 30
-
-
方法的使用细节和注意事项
-
基本使用
-
访问修饰符返回数据类型方法名(形参列表…){//方法体
语句;
return返回值;
}
-
-
注意事项
- 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组 ]
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和return的 值类型一致或兼容
- 如果方法是void,则方法体中可以没有return语句,或者 只写 return;
-
代码示例
-
public class MethodDetail { public static void main(String[] args) { AA a = new AA(); int[] res = a.getSumAndSub(1, 4); System.out.println("和 = " + res[0]); System.out.println("差 = " + res[1]); // 细节: 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数 byte b1 = 1; byte b2 = 2; a.getSumAndSub(b1, b2); // byte -> int // a.getSumAndSub(1.1, 1.8); // double -> int (×) // 细节: 实参和形参的类型要一致或兼容、个数、顺序必须一致 // a.getSumAndSub(100); // × 个数不一致 a.f3("tom", 10); // ok // a.f3(100, "jack"); // 实际参数和形式参数顺序不对 } } class AA { // 细节: 方法不能嵌套定义 public void f4() { // 错误 // public void f5() { // } } public void f3(String str, int n) { } // 1. 一个方法最多有一个返回值 [思考,如何返回多个结果,返回数组] public int[] getSumAndSub(int n1, int n2) { int[] resArr = new int[2]; resArr[0] = n1 + n2; resArr[1] = n1 - n2; return resArr; } // 2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象) // 具体看 getSumAndSub // 3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; // 而且要求返回值类型必须和 return 的值类型一致或兼容 public double f1() { double d1 = 1.1 * 3; int n = 100; return n; // int -> double // return d1; // ok? double -> int } // 最好是见名知意 public void f2() { // 如果方法是 void,则方法体中可以没有 return 语句,或者只写 return; // 老韩提示:在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义 System.out.println("hello1"); System.out.println("hello1"); System.out.println("hello1"); int n = 10; // return; } } //和 = 5 //差 = -3
-
-
-
传参机制
-
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参!
-
public class hello { // 编写一个main方法 public static void main(String[] args) { int a = 10; int b = 20; // 创建AA对象名字obj AA obj = new AA(); obj.swap(a, b); // 调用swap System.out.println("main方法a=" + a + " b=" + b); // a=10 b=20 } } class AA { public void swap(int a, int b) { System.out.println("\na和b交换前的值\na=" + a + "\tb=" + b); // a=10 b=20 // 完成了a和b的交换 int tmp = a; a = b; b = tmp; System.out.println("\na和b交换后的值\na=" + a + "\tb=" + b); // a=20 b=10 } } 输出结果: a和b交换前的值 a=10 b=20 a和b交换后的值 a=20 b=10 main方法a=10 b=20
-
-
B类中编写一个方法test100,可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化?会变化 ;B类中编写一个方法test200,可以接收一个Person(age,sal)对象,在方法中修改该对象属性,看看原来的对象是否变化?会变化;引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
-
public class hello { // 编写一个main方法 public static void main(String[] args) { // 测试 B b = new B(); // Corrected the instantiation of class B int[] arr = {1, 2, 3}; b.test100(arr); // 调用方法 System.out.println("main 的 arr 数组 "); // 遍历数组 for(int i = 0; i < arr.length; i++) { System.out.print(arr[i] + "\t"); } System.out.println(); // Moved inside the for loop to correct syntax // 测试 Person p = new Person(); p.name = "jack"; p.age = 10; b.test200(p); // 测试题, 如果 test200 执行的是 p=null,下面的结果是 10 // 测试题, 如果 test200 执行的是 p=new Person();..., 下面输出的是10 System.out.println("main 的 p.age=" + p.age); // 10 is printed because the object reference is not changed in test200 } } class Person { String name; int age; } class B { public void test200(Person p) { p.age = 10000; // 修改对象属性 // 可以选择下面的代码来测试不同的行为 // p = new Person(); // This will not affect 'p' in the main method // p.name = "tom"; // p.age = 99; // p = null; // This will not affect 'p' in the main method } // B 类中编写一个方法test100, // 可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化 public void test100(int[] arr) { arr[0] = 200; // 修改元素 // 遍历数组 System.out.println("test100 的 arr 数组 "); for(int i = 0; i < arr.length; i++) { System.out.print(arr[i] + "\t"); } System.out.println(); } } 输出结果: test100 的 arr 数组 200 2 3 main 的 arr 数组 200 2 3 main 的 p.age=10000
-
-
-
递归机制
-
介绍
- 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变 得简洁
-
代码示例
-
public class hello { //编写一个main方法 public static void main(String[] args) { T t1 = new T(); t1.test(4); // 输出 n=2 n=3 n=4 int res = t1.factorial(5); System.out.println("5 的阶乘 res = " + res); } } class T { // 分析 public void test(int n) { if (n > 2) { test(n - 1); // 递归调用 } System.out.println("n=" + n); } // factorial 阶乘 public int factorial(int n) { if (n == 1) { return 1; } else { return factorial(n - 1) * n; } } } 输出结果: n=2 n=3 n=4 5 的阶乘 res = 120
-
递归斐波那契函数
-
请使用递归的方式求出斐波那契数1,1,2,3,5,8,13…给你一个整数n,求出它的值是多少
-
猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第10天时,想再吃时(即还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
-
public class hello { // 编写一个 main 方法 public static void main(String[] args) { T t1 = new T(); // 斐波那契数列问题 int n = 7; int res = t1.fibonacci(n); if (res != -1) { System.out.println("当 n = " + n + " 对应的斐波那契数 = " + res); } // 桃子问题 int day = 1; int peachNum = t1.peach(day); if (peachNum != -1) { System.out.println("第 " + day + " 天有 " + peachNum + " 个桃子"); } } } class T { /* 请使用递归的方式求出斐波那契数 1, 1, 2, 3, 5, 8, 13... 给你一个整数 n,求出它的值 思路分析: 1. 当 n = 1,斐波那契数是 1 2. 当 n = 2,斐波那契数是 1 3. 当 n >= 3,斐波那契数是前两个数的和 4. 这就是一个递归的思路 */ public int fibonacci(int n) { if (n >= 1) { if (n == 1 || n == 2) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } else { System.out.println("要求输入的 n >= 1 的整数"); return -1; } } /* 猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个! 以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时, 想再吃时(即还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子? 思路分析(逆推): 1. day = 10 时,有 1 个桃子 2. day = 9 时,有 (day10 + 1) * 2 = 4 个桃子 3. day = 8 时,有 (day9 + 1) * 2 = 10 个桃子 4. 规律就是:前一天的桃子 = (后一天的桃子 + 1) * 2 5. 递归 */ public int peach(int day) { if (day == 10) { // 第 10 天,只有 1 个桃子 return 1; } else if (day >= 1 && day <= 9) { return (peach(day + 1) + 1) * 2; } else { System.out.println("day 在 1-10 之间"); return -1; } } } 输出结果: 当 n = 7 对应的斐波那契数 = 13 第 1 天有 1534 个桃子
-
-
((20240506132650-lz0vjc3 ‘走出迷宫’))
-
((20240506181958-4dv5m3n ‘汉诺塔’))
-
-
-
原理图
方法重载
-
介绍
- java中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!比如: System.out.printin“hello”); out是PrintStream类型
-
代码示例
-
public class hello { // 编写一个 main 方法 public static void main(String[] args) { // 调用不同的 calculate 方法,验证方法重载 MyCalculator mc = new MyCalculator(); System.out.println(mc.calculate(1, 2)); // 调用 calculate(int, int) System.out.println(mc.calculate(1.1, 2)); // 调用 calculate(double, int) System.out.println(mc.calculate(1, 2.1)); // 调用 calculate(int, double) System.out.println(mc.calculate(1, 2, 3)); // 调用 calculate(int, int, int) } } class MyCalculator { // 下面的四个 calculate 方法构成了重载 // 1. 两个整数的和 public int calculate(int n1, int n2) { System.out.println("calculate(int n1, int n2) 被调用"); return n1 + n2; } // 2. 一个整数和一个 double 的和 public double calculate(int n1, double n2) { System.out.println("calculate(int n1, double n2) 被调用"); return n1 + n2; } // 3. 一个 double 和一个整数的和 public double calculate(double n1, int n2) { System.out.println("calculate(double n1, int n2) 被调用"); return n1 + n2; } // 4. 三个整数的和 public int calculate(int n1, int n2, int n3) { System.out.println("calculate(int n1, int n2, int n3) 被调用"); return n1 + n2 + n3; } } 输出结果: calculate(int n1, int n2) 被调用 3 calculate(double n1, int n2) 被调用 3.1 calculate(int n1, double n2) 被调用 3.1 calculate(int n1, int n2, int n3) 被调用 6
-
-
注意事项
- 方法名:必须相同
- 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
- 返回类型:无要求
可变参数
-
基本概念
- java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现
-
基本语法
- 访问修饰符 返回类型 方法名(数据类型…形参名){ }
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { HspMethod m = new HspMethod(); System.out.println(m.sum(1, 5, 100)); // 106 System.out.println(m.sum(1, 19)); // 20 } } class HspMethod { // 使用可变参数来计算多个数的和 // 1. int... 表示接收可变数量的 int 类型参数 // 2. nums 可当作数组来使用 // 3. 遍历 nums 进行求和 public int sum(int... nums) { // System.out.println("接收的参数个数=" + nums.length); int res = 0; for (int i = 0; i < nums.length; i++) { res += nums[i]; } return res; } } //106 //20
-
有三个方法,分别实现返回姓名和两门课成绩(总分),返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。封装成一个可变参数的方法
-
public class hello { // 编写一个main方法 public static void main(String[] args) { HspMethod hm = new HspMethod(); System.out.println(hm.showScore("milan", 90.1, 80.0)); // milan有2门课的成绩总分为=170.1 System.out.println(hm.showScore("terry", 90.1, 80.0, 10, 30.5, 70)); // terry有5门课的成绩总分为=280.6 } } class HspMethod { /* 有三个方法,分别实现返回姓名和两门课成绩(总分), 返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。 封装成一个可变参数的方法 分析: 1. 方法名 showScore 2. 形参(String, double...) 3. 返回 String */ public String showScore(String name, double... scores) { double totalScore = 0; for (int i = 0; i < scores.length; i++) { totalScore += scores[i]; } return name + "有" + scores.length + "门课的成绩总分为=" + totalScore; } } //milan有2门课的成绩总分为=170.1 //terry有5门课的成绩总分为=280.6
-
-
-
注意事项
- 可变参数的实参可以为0个或任意多个。
- 可变参数的实参可以为数组。可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
作用域
-
介绍
- 面向对象中,变量作用域是非常重要知识点,相对来说不是特别好理解,请大家注意听,认真思考,要求深刻掌握变量作用域。
-
注意事项
- 全局变量(属性)可以不赋值,局部变量必须赋值后,才能使用,因为没有默认值
- 属性和局部变量可以重名,访问时遵循就近原则。
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
- 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。即在一次方法调用过程中。
- 作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)局部变量:只能在本类中对应的方法中使用 - 修饰符不同
全局变量/属性可以加修饰符,局部变量不可以加修饰符
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { Person p1 = new Person(); /* 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。 局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。 即在一次方法调用过程中。 */ // p1.say(); // 当执行 say 方法时,say 方法的局部变量比如 name 会创建,当 say 执行完毕后, // name 局部变量就销毁,但是属性(全局变量)仍然可以使用。 T t1 = new T(); t1.test(); // 第 1 种跨类访问对象属性的方式 t1.test2(p1); // 第 2 种跨类访问对象属性的方式 } } class T { // 全局变量/属性:可以被本类使用,或通过对象被其他类使用 public void test() { Person p1 = new Person(); System.out.println(p1.name); // 输出: jack } public void test2(Person p) { System.out.println(p.name); // 输出: jack } } class Person { // 细节: 属性可以加修饰符 (public, protected, private...) // 局部变量不能加修饰符 public int age = 20; String name = "jack"; public void say() { // 细节: 属性和局部变量可以重名,访问时遵循就近原则 String name = "king"; System.out.println("say() name=" + name); // 输出: king (就近原则,使用局部变量) } public void hi() { String address = "北京"; // String address = "上海"; // 错误,重复定义变量 String name = "hsp"; // 这是可以的,局部变量和全局变量同名,使用局部变量 } } //jack //jack
-
构造器
-
介绍
-
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
-
-
语法
- [修饰符] 方法名(形参列表){
方法体;
}
- [修饰符] 方法名(形参列表){
-
注意事项
- 一个类可以定义多个不同的构造器,即构造器重载
比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名不需要指定年龄 - 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化,并不是创建对象
- 在创建对象时,系统自动的调用该类的构造方法
- 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如Dog (),使用javap指令 反编译看看
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即:Dog()0写(这点很重要)
- 一个类可以定义多个不同的构造器,即构造器重载
-
代码示例
-
在创建人类的对象时,就直接指定这个对象的年龄和姓名
-
public class hello { // 编写一个main方法 public static void main(String[] args) { // 当我们 new 一个对象时,直接通过构造器指定名字和年龄 Person p1 = new Person("smith", 80); System.out.println("p1 的信息如下"); System.out.println("p1 对象 name=" + p1.name); // smith System.out.println("p1 对象 age=" + p1.age); // 80 } } // 在创建 Person 类的对象时,直接指定这个对象的年龄和姓名 class Person { String name; int age; // 构造器 // 老韩解读: // 1. 构造器没有返回值,也不能写 void // 2. 构造器的名称和类名 Person 一样 // 3. (String pName, int pAge) 是构造器的形参列表,规则和成员方法一样 public Person(String pName, int pAge) { System.out.println("构造器被调用~~ 完成对象的属性初始化"); name = pName; age = pAge; } } 输出结果: 构造器被调用~~ 完成对象的属性初始化 p1 的信息如下 p1 对象 name=smith p1 对象 age=80
-
-
public class hello { // 编写一个main方法 public static void main(String[] args) { Person p1 = new Person("king", 40); // 第1个构造器 Person p2 = new Person("tom"); // 第2个构造器 Dog dog1 = new Dog(); // 使用的是默认的无参构造器 } } class Dog { // 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器) // 使用javap 指令 反编译看看 // 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器, // 除非显式的定义一下,即: Dog(){} 写 (这点很重要) // 显式的定义一下无参构造器 Dog() { } // 带参数的构造器 public Dog(String dName) { //... } } class Person { String name; int age; // 默认 0 // 第1个构造器 public Person(String pName, int pAge) { name = pName; age = pAge; } // 第2个构造器,只指定人名,不需要指定年龄 public Person(String pName) { name = pName; } } 结果:编译成功
-
第一个无参构造器:利用构造器设置所有人的age属性初始值都为18
第二个带pName和pAge两个参数的构造器:使得每次创建Person对象的同时初始化对象的age属性值和name属性值。 分别使用不同的构造器,创建对象-
public class hello { // 编写一个main方法 public static void main(String[] args) { Person p1 = new Person(); // 无参构造器 // 下面输出 name=null, age=18 System.out.println("p1 的信息 name=" + p1.name + " age=" + p1.age); Person p2 = new Person("scott", 50); // 下面输出 name=scott, age=50 System.out.println("p2 的信息 name=" + p2.name + " age=" + p2.age); } } /** * 在前面定义的Person类中添加两个构造器: * 第一个无参构造器:利用构造器设置所有人的age属性初始值都为18 * 第二个带pName和pAge两个参数的构造器: * 使得每次创建Person对象的同时初始化对象的age属性值和name属性值。 * 分别使用不同的构造器,创建对象. */ class Person { String name; // 默认值 null int age; // 默认 0 public Person() { // 第一个无参构造器:利用构造器设置所有人的age属性初始值都为18 age = 18; } // 第二个带pName和pAge两个参数的构造器 public Person(String pName, int pAge) { name = pName; age = pAge; } } //p1 的信息 name=null age=18 //p2 的信息 name=scott age=50
-
文件:hello.java 无参构造器的使用: public class hello { private int number; private String name; // 显式定义的无参构造器 public hello() { this.number = 0; this.name = "Default Name"; System.out.println("无参构造器被调用,对象已创建"); } // 方法示例 public void displayInfo() { System.out.println("Number: " + number + ", Name: " + name); } public static void main(String[] args) { hello obj = new hello(); obj.displayInfo(); } } //无参构造器被调用,对象已创建 //Number: 0, Name: Default Name
-
-
包
-
介绍
- 包的本质实际上就是创建不同的文件夹来保存类文件
-
常用的包
- java.lang.* //lang包是基本包,默认引入,不需要再引入.
- java.util.* //util 包,系统提供的工具包,工具类,使用 Scanner
- java.net.* //网络包,网络开发
- java.awt.* //是做java的界面开发,GUI
-
注意事项
- package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
- import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
-
代码示例
-
基本使用
-
文件结构: D:. └─com └─hspedu └─pkg └─pkgdetail.java pkgdetail.java代码: // package的作用是声明当前类所在的包,需要放在类(或者文件)的最上面, // 一个类中最多只有一句package package com.hspedu.pkg; // import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求 import java.util.Scanner; import java.util.Arrays; // 类定义 public class pkgdetail { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int[] arr = {0, -1, 1}; Arrays.sort(arr); // 应该对数组arr进行排序,而不是args } } 结果:编译不报错
-
-
-
访问修饰符
-
注意事项
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。
- 成员方法的访问规则和属性完全一样
-
代码示例
-
文件结构: └─src └─com └─hspedu ├─modifier └─A └─B └─test A类: package com.hspedu.modifier; public class A { // 四个属性,分别使用不同的访问修饰符来修饰 public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public void m1() { // 在同一类中,可以访问public, protected, 默认, private 修饰的属性和方法 System.out.println("n1=" + n1 + " n2=" + n2 + " n3=" + n3 + " n4=" + n4); } protected void m2() { } void m3() { } private void m4() { } public void hi() { // 在同一类中,可以访问public, protected, 默认, private 修饰的属性和方法 m1(); m2(); m3(); m4(); } } B类: package com.hspedu.modifier; public class B { public void say() { A a = new A(); // 在同一个包下,可以访问public, protected和默认修饰属性或方法,不能访问private属性或方法 System.out.println("n1=" + a.n1 + " n2=" + a.n2 + " n3=" + a.n3); a.m1(); a.m2(); a.m3(); // a.m4(); // 错误的,因为m4是私有方法 } } test类: package com.hspedu.modifier; public class Test { public static void main(String[] args) { A a = new A(); a.m1(); B b = new B(); b.say(); } } // 只有默认和public可以修饰类 class Tiger {} 运行test类结果: n1=100 n2=200 n3=300 n4=400 n1=100 n2=200 n3=300 n1=100 n2=200 n3=300 n4=400
-
对象编程三大特征
封装
-
介绍
- 面向对象编程有三大特征:封装、继承和多态。
- 封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
-
步骤
- 将属性进行私有化private【不能直接修改属性】
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型参数名){/Xxx表示某个属性
/加入数据验证的业务逻辑
属性=参数名;
} - 提供一个公共的(public)get方法,用于获取属性的值
public 数据类型getXxx(){/权限判断,Xxx某个属性
return xx;
}
-
代码示例
-
请大家看一个小程序,不能 随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认年龄,必须在1—120,年龄,工资不能直接查看,
name的长度在2—6字符之间-
package com.com.hspedu; public class test { public static void main(String[] args) { // 如果要使用快捷键alt+r,需要先配置主类 // 第一次,我们使用鼠标点击形式运行程序,后面就可以用 Person person = new Person(); person.setName("cong"); person.setAge(30); person.setSalary(30000); System.out.println(person.info()); System.out.println(person.getSalary()); // 如果我们自己使用构造器指定属性 Person smith = new Person("smith", 80, 50000); System.out.println("====smith的信息======"); System.out.println(smith.info()); } } class Person { public String name; // 名字公开 private int age; // age 私有化 private double salary; // 私有化 // 构造器 public Person() { } // 有三个属性的构造器 public Person(String name, int age, double salary) { setName(name); setAge(age); setSalary(salary); } public String getName() { return name; } public void setName(String name) { // 加入对数据的校验,相当于增加了业务逻辑 if (name.length() >= 2 && name.length() <= 6) { this.name = name; } else { this.name = "无名人"; System.out.println("名字的长度不对,需要(2-6)个字符,默认名字"); } } public int getAge() { return age; } public void setAge(int age) { // 判断 if (age >= 1 && age <= 120) { // 如果是合理范围 this.age = age; } else { System.out.println("你设置的年龄不对,需要在 (1-120),给默认年龄18"); this.age = 18; // 给一个默认年龄 } } public double getSalary() { // 可以这里增加对当前对象的权限判断 return salary; } public void setSalary(double salary) { this.salary = salary; } // 写一个方法,返回属性信息 public String info() { return "Name: " + name + ", Age: " + age + ", Salary: " + salary; } } 输出结果: Name: cong, Age: 30, Salary: 30000.0 30000.0 ====smith的信息====== Name: smith, Age: 80, Salary: 50000.0
-
-
继承
-
介绍
- 继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中 抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。画出继承的示意图
-
代码示例
-
文件结构: └─src └─com └─hspedu ├─extend_ └─improve_ └─extends01 └─Student └─Pupil └─graduate ================================================================================================================== student类(父类): package com.hspedu.extend_.improve_; // 父类,是Pupil 和 Graduate 的父类 public class Student { // 共有属性 public String name; public int age; private double score; // 成绩 // 共有的方法 public void setScore(double score) { this.score = score; } public void showInfo() { System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score); } } ================================================================================================================== pupil(继承student的子类): package com.hspedu.extend_.improve_; // 让 Pupil 继承 Student 类 public class Pupil extends Student { public void testing() { System.out.println("小学生 " + name + " 正在考小学数学.."); } } ================================================================================================================== graduate类(继承student的子类): package com.hspedu.extend_.improve_; public class Graduate extends Student { public void testing() { // 和Pupil不一样 System.out.println("大学生 " + name + " 正在考大学数学.."); } } ================================================================================================================== extends01类: package com.hspedu.extend_.improve_; import com.hspedu.extend_.Graduate; import com.hspedu.extend_.Pupil; public class Extends01 { public static void main(String[] args) { com.hspedu.extend_.Pupil pupil = new Pupil(); pupil.name = "银角大王~"; pupil.age = 11; pupil.testing(); pupil.setScore(50); pupil.showInfo(); System.out.println("======="); com.hspedu.extend_.Graduate graduate = new Graduate(); graduate.name = "金角大王~"; graduate.age = 23; graduate.testing(); graduate.setScore(80); graduate.showInfo(); } } 输出结果: 小学生 银角大王~ 正在考小学数学.. 学生名 银角大王~ 年龄 11 成绩 50.0 ======= 大学生 金角大王~ 正在考大学数学.. 学生名 金角大王~ 年龄 23 成绩 80.0
-
-
使用细节
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
- super在使用时,必须放在构造器第一行(super只能在构造器中使用)
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类,Object是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。 思考:如何让A类继承B类和C类?【A继承B,B继承C】
- 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
-
代码示例
-
文件结构: └─src └─com └─hspedu └─extend_ └─improve_ └─ExtendsDetail.java └─TopBase.java └─Base.java └─Sub.java 继承结构: TopBase.java └─Base.java └─Sub.java ============================================================================================================================================================================ TopBase.java代码: package src.com.hspedu.extend_.improve_; public class TopBase { // 父类是 Object public TopBase() { //super(); // Object 的无参构造器,可以省略这行 System.out.println("构造器 TopBase() 被调用..."); // 1 } } ================================================================================================================================ Base.java代码: package src.com.hspedu.extend_.improve_; public class Base extends TopBase { // 父类 // 4 个属性 public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public Base() { // 无参构造器 System.out.println("父类 Base() 构造器被调用...."); } public Base(String name, int age) { // 有参构造器 // 默认super() System.out.println("父类 Base(String name, int age)构造器被调用...."); } public Base(String name) { // 有参构造器 System.out.println("父类 Base(String name)构造器被调用...."); } // 父类提供一个 public 的方法,返回了 n4 public int getN4() { return n4; } public void test100() { System.out.println("test100"); } protected void test200() { System.out.println("test200"); } void test300() { System.out.println("test300"); } private void test400() { System.out.println("test400"); } // call public void callTest400() { test400(); } } ================================================================================================================================ Sub.java代码: package src.com.hspedu.extend_.improve_; import java.util.Arrays; // 输入ctrl + H 可以看到类的继承关系 public class Sub extends Base { // 子类 public Sub(String name, int age) { // 1. 老师要调用父类的无参构造器, 如下或者什么都不写,默认就是调用super() // super(); // 父类的无参构造器 // 2. 老师要调用父类的 Base(String name) 构造器 // super("hsp"); // 3. 老师要调用父类的 Base(String name, int age) 构造器 super("king", 20); // 细节:super 在使用时,必须放在构造器第一行 // 细节: super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器 // this() 不能再使用了 System.out.println("子类 Sub(String name, int age)构造器被调用...."); } public Sub() { // 无参构造器 // super(); // 默认调用父类的无参构造器 super("smith", 10); System.out.println("子类 Sub()构造器被调用...."); } public Sub(String name) { super("tom", 30); // do nothing... System.out.println("子类 Sub(String name)构造器被调用...."); } public void sayOk() { // 子类方法 // 非私有的属性和方法可以在子类直接访问 // 但是私有属性和方法不能在子类直接访问 System.out.println(n1 + " " + n2 + " " + n3); test100(); test200(); test300(); // test400(); 错误 // 要通过父类提供公共的方法去访问 System.out.println("n4=" + getN4()); callTest400(); } } ================================================================================================================================ ExtendsDetail.java代码: package src.com.hspedu.extend_.improve_; public class ExtendsDetail { public static void main(String[] args) { // System.out.println("===第1个对象===="); // Sub sub = new Sub(); // 创建了子类对象sub // System.out.println("===第2个对象===="); // Sub sub2 = new Sub("jack"); // 创建了子类对象sub2 System.out.println("===第3个对象===="); Sub sub3 = new Sub("king", 10); // 创建了子类对象sub3 // sub.sayOk(); } } 执行结果: ===第3个对象==== 构造器 TopBase() 被调用... 父类 Base(String name, int age)构造器被调用.... 子类 Sub(String name, int age)构造器被调用....
-
public class hello { public static void main(String[] args){ B b=new B(); } } class A { A() { System.out.println("a"); } A(String name) { System.out.println("a name"); } } class B extends A{ B(){ this("abc"); System.out.println("b"); } B(String name){ System.out .println("b name"); } } 输出结果: a b name b
-
public class hello { public static void main(String[] args) { C c = new C(); } } class A { // A 类 public A() { System.out.println("我是 A 类"); } } class B extends A { // B 类, 继承 A 类 public B() { System.out.println("我是 B 类的无参构造"); } public B(String name) { System.out.println(name + " 我是 B 类的有参构造"); } } class C extends B { // C 类,继承 B 类 public C() { this("hello"); System.out.println("我是 C 类的无参构造"); } public C(String name) { super("hahah"); System.out.println("我是 C 类的有参构造"); } } 输出结果: 我是 A 类 hahah 我是 B 类的有参构造 我是 C 类的有参构造 我是 C 类的无参构造
-
-
多态
-
介绍
- Java的多态是面向对象编程的核心概念之一,它允许不同类的对象对同一消息做出响应。具体来说,多态性使得父类引用可以指向子类对象,从而在运行时根据实际对象类型调用相应的方法,而不是编译时的引用类型。这种机制增强了代码的灵活性和可扩展性,使得程序可以更容易地适应变化,同时也提高了代码的重用性。多态通过方法重写、接口实现和抽象类等方式来实现,是Java语言实现"一个接口,多个方法"的重要手段。
-
代码示例
-
文件结构: └─src └─com └─hspedu └─poly_ └─Animal.java └─Bone.java └─Cat.java └─Dog.java └─Fish.java └─Food.java └─Master.java └─Poly01.java └─Rice.java └─Pig.java 继承关系: Food └─Bone └─Rice └─Fish Animal └─Pig └─Cat └─Dog Animal.java: package com.hspedu.poly_; public class Animal { private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } =========================================================================================== Food.java: package com.hspedu.poly_; public class Food { private String name; public Food(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } =========================================================================================== Master.java: package com.hspedu.poly_; public class Master { private String name; public Master(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } //使用多态机制,可以统一的管理主人喂食的问题 //animal 编译类型是Animal,可以指向(接收) Animal子类的对象 //food 编译类型是Food ,可以指向(接收) Food子类的对象 public void feed(Animal animal, Food food) { System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName()); } //主人给小狗 喂食 骨头 // public void feed(Dog dog, Bone bone) { // System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName()); // } // //主人给 小猫喂 黄花鱼 // public void feed(Cat cat, Fish fish) { // System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName()); // } //如果动物很多,食物很多 //===> feed 方法很多,不利于管理和维护 //Pig --> Rice //Tiger ---> meat ... //... } =========================================================================================== Bone.java: package com.hspedu.poly_; public class Bone extends Food { public Bone(String name) { super(name); } } =========================================================================================== Cat.java: package com.hspedu.poly_; public class Cat extends Animal { public Cat(String name) { super(name); } } =========================================================================================== Dog.java: package com.hspedu.poly_; public class Dog extends Animal { public Dog(String name) { super(name); } } =========================================================================================== Fish.java: package com.hspedu.poly_; public class Fish extends Food { public Fish(String name) { super(name); } } =========================================================================================== Pig.java: package com.hspedu.poly_; public class Pig extends Animal { public Pig(String name) { super(name); } } =========================================================================================== Rice.java: package com.hspedu.poly_; public class Rice extends Food { public Rice(String name) { super(name); } } =========================================================================================== poly01.java: package com.hspedu.poly_; public class Poly01 { public static void main(String[] args) { Master tom = new Master("汤姆"); Dog dog = new Dog("大黄~"); Bone bone = new Bone("大棒骨~"); tom.feed(dog, bone); Cat cat = new Cat("小花猫~"); Fish fish = new Fish("黄花鱼~"); System.out.println("===========-------"); tom.feed(cat, fish); //添加 给小猪为米饭 Pig pig = new Pig("小花猪"); Rice rice = new Rice("米饭"); System.out.println("==================="); tom.feed(pig, rice); } } 输出结果: 主人 汤姆 给 大黄~ 吃 大棒骨~ ===========------- 主人 汤姆 给 小花猫~ 吃 黄花鱼~ =================== 主人 汤姆 给 小花猪 吃 米饭
-
多态的向上/下转型
-
介绍
- 父类的引用指向了子类的对象
-
基本语法
-
向上转型
- 父类类型 引用名 = new 子类类型();
-
向下转型
- 子类类型 引用名 = (子类类型)父类引用;
-
-
特点
-
向上转型
- 编译类型看左边,运行类型看右边
- 可以调用父类中所有成员(需遵守访问权限)
- 最终运行效果看子类的具体实现
-
向下转型
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
-
-
代码示例
-
文件结构: └─src └─com └─hspedu └─poly_ └─detail_ └─Animal.java └─Cat.java └─PolyDetail.java └─Dog.java 继承关系: Animal └─Cat └─Dog Animal。java: package src.com.hspedu.poly_.detail_; public class Animal { String name = "动物"; int age = 10; public void sleep(){ System.out.println("睡"); } public void run(){ System.out.println("跑"); } public void eat(){ System.out.println("吃"); } public void show(){ System.out.println("hello,你好"); } } ================================================================ Cat.java: package src.com.hspedu.poly_.detail_; public class Cat extends Animal { public void eat(){//方法重写 System.out.println("猫吃鱼"); } public void catchMouse(){//Cat特有方法 System.out.println("猫抓老鼠"); } } ================================================================ Dog.java: package src.com.hspedu.poly_.detail_; public class Dog extends Animal {//Dog是Animal的子类 } ================================================================ PolyDetail.java: package src.com.hspedu.poly_.detail_; public class PolyDetail { public static void main(String[] args) { //向上转型: 父类的引用指向了子类的对象 //语法:父类类型引用名 = new 子类类型(); Animal animal = new Cat(); Object obj = new Cat();//可以吗? 可以 Object 也是 Cat的父类 //向上转型调用方法的规则如下: //(1)可以调用父类中的所有成员(需遵守访问权限) //(2)但是不能调用子类的特有的成员 //(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的 //animal.catchMouse();错误 //(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法 //,然后调用,规则我前面我们讲的方法调用规则一致。 animal.eat();//猫吃鱼.. animal.run();//跑 animal.show();//hello,你好 animal.sleep();//睡 //animal.catchMouse();//报错 System.out.println("============"); //老师希望,可以调用Cat的 catchMouse方法 //多态的向下转型 //(1)语法:子类类型 引用名 =(子类类型)父类引用; //问一个问题? cat 的编译类型 Cat,运行类型是 Cat Cat cat = (Cat) animal; cat.catchMouse();//猫抓老鼠 cat.run();//跑 //(2)要求父类的引用必须指向的是当前目标类型的对象 //Dog dog = (Dog) animal; //可以吗?不可以,因为猫与狗没有任何关系,无法转型 } } 输出结果: 猫吃鱼 跑 hello,你好 睡 ============ 猫抓老鼠 跑
-
public class hello { public static void main(String[] args) { Sub s = new Sub(); System.out.println(s.count);//20 s.display();//20 Base b = s; System.out.println(b == s);//T System.out.println(b.count);//10 System.out.println(((Sub)b).count); // 这会输出 20 b.display();//20 } } class Base {//父类 int count = 10; public void display() { System.out.println(this.count); } } class Sub extends Base {//子类 int count = 20; public void display() { System.out.println(this.count); } }
-
-
-
动态绑定
-
介绍
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
-
代码示例
-
package src.com.hspedu.poly_.dynamic_; public class DynamicBinding { public static void main(String[] args) { //a 的编译类型 A, 运行类型 B A a = new B();//向上转型 System.out.println(a.sum());// 30 System.out.println(a.sum1());// 20 } } class A {//父类 public int i = 10; //动态绑定机制: public int sum() {//父类sum() return getI() + 10;//20 + 10 } public int sum1() {//父类sum1() return i + 10;//10 + 10 } public int getI() {//父类getI return i; } } class B extends A {//子类 public int i = 20; // public int sum() { // return i + 20; // } public int getI() {//子类getI() return i; } // public int sum1() { // return i + 10; // } }
-
-
-
多态数组
-
介绍
- 数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
-
代码示例
-
要求: 现有一个继承结构如下:要求创建1个Person对象、2个Student对象和2个Teacher对象,统一放在数组 中,并调用每个对象 ==================================================================================== Person.java: package src.com.hspedu.poly_.polyarr_; public class Person {//父类 private String name; private int age; public Person(String name, int age) { this.name = name; this.age = 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 String say() {//返回名字和年龄 return name + "\t" + age; } } ==================================================================================== Student.java: package src.com.hspedu.poly_.polyarr_; public class Student extends Person { private double score; public Student(String name, int age, double score) { super(name, age); this.score = score; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } //重写父类say @Override public String say() { return "学生 " + super.say() + " score=" + score; } //特有的方法 public void study() { System.out.println("学生 " + getName() + " 正在学java..."); } } ==================================================================================== Teacher.java: package src.com.hspedu.poly_.polyarr_; public class Teacher extends Person { private double salary; public Teacher(String name, int age, double salary) { super(name, age); this.salary = salary; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } //写重写父类的say方法 @Override public String say() { return "老师 " + super.say() + " salary=" + salary; } //特有方法 public void teach() { System.out.println("老师 " + getName() + " 正在讲java课程..."); } } ==================================================================================== PloyArray.java: package src.com.hspedu.poly_.polyarr_; public class Teacher extends Person { private double salary; public Teacher(String name, int age, double salary) { super(name, age); this.salary = salary; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } //写重写父类的say方法 @Override public String say() { return "老师 " + super.say() + " salary=" + salary; } //特有方法 public void teach() { System.out.println("老师 " + getName() + " 正在讲java课程..."); } } 输出结果: jack 20 学生 mary 18 score=100.0 学生 mary 正在学java... 学生 smith 19 score=30.1 学生 smith 正在学java... 老师 scott 30 salary=20000.0 老师 scott 正在讲java课程... 老师 king 50 salary=25000.0 老师 king 正在讲java课程...
-
-
-
this关键字
-
介绍
- java虚拟机会给每个对象分配 this,代表当前对象。
-
代码示例
-
public class hello { // 编写一个main方法 public static void main(String[] args) { Dog dog1 = new Dog("大壮", 3); System.out.println("dog1 的 hashcode=" + dog1.hashCode()); // dog1 调用了 info() 方法 dog1.info(); System.out.println("============"); Dog dog2 = new Dog("大黄", 2); System.out.println("dog2 的 hashcode=" + dog2.hashCode()); dog2.info(); } } class Dog { // 类 String name; int age; // public Dog(String dName, int dAge){//构造器 // name = dName; //age = dAge; name = dName; // 构造器 public Dog(String name, int age) { // 使用this关键字区分局部变量和实例变量 this.name = name; this.age = age; System.out.println("this.hashCode=" + this.hashCode()); } // 成员方法,输出属性信息 public void info() { System.out.println("this.hashCode=" + this.hashCode()); System.out.println(name + "\t" + age + "\t"); } } 输出结果: this.hashCode=990368553 dog1 的 hashcode=990368553 this.hashCode=990368553 大壮 3 ============ this.hashCode=1595428806 dog2 的 hashcode=1595428806 this.hashCode=1595428806 大黄 2
-
原理图
-
super关键字
-
介绍
- super代表父类的引用,用于访问父类的属性、方法、构造器
-
注意细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super,如果没有重名,使用super,this、直接访问是一样的效果!
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A—>B—>C,当然也需要遵守访问权限的相关规则
-
代码示例
-
文件结构: └─src └─com └─hspedu └─super_ └─A.java └─B.java └─Base.java └─Super01.java └─A.java 继承机构: Base.java └─A.java └─B.java ====================================================================================================== Base.java: package src.com.hspedu.super_; public class Base { // 父类是Object public int n1 = 999; public int age = 111; public void cal() { System.out.println("Base类的cal()方法..."); } public void eat() { System.out.println("Base类的eat()....."); } } ====================================================================================================== A.java: package src.com.hspedu.super_; public class A extends Base { // 4个属性 // public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public A() {} public A(String name) {} public A(String name, int age) {} // public void cal() { // System.out.println("A类的cal()方法..."); // } public void test100() {} protected void test200() {} void test300() {} private void test400() {} } ====================================================================================================== B.java: package src.com.hspedu.super_; public class B extends A { public int n1 = 888; // 编写测试方法 public void test() { // super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员; // 如果多个基类(上级类)中都有同名的成员,使用 super 访问遵循就近原则。A->B->C System.out.println("super.n1=" + super.n1); super.cal(); } // 访问父类的属性, 但不能访问父类的 private 属性 [案例] super.属性名 public void hi() { System.out.println(super.n1 + " " + super.n2 + " " + super.n3); } public void cal() { System.out.println("B 类的 cal() 方法..."); } public void sum() { System.out.println("B 类的 sum()"); // 希望调用父类-A 的 cal 方法 // 这时,因为子类 B 没有 cal 方法,因此我可以使用下面三种方式 // 找 cal 方法时(cal() 和 this.cal()),顺序是: // (1)先找本类,如果有,则调用 // (2)如果没有,则找父类(如果有,并可以调用,则调用) // // cal(); this.cal(); // 等价 cal // (3)如果父类没有,则继续找父类的父类, 整个规则,就是一样的,直到 Object 类 // 提示:如果查找方法的过程中,找到了,但是不能访问,则报错, cannot access // 如果查找方法的过程中,没有找到,则提示方法不存在 // 找 cal 方法(super.cal()) 的顺序是直接查找父类,其他的规则一样 // super.cal(); // 演示访问属性的规则 // n1 和 this.n1 查找的规则是 // (1) 先找本类,如果有,则调用 // (2) 如果没有,则找父类(如果有,并可以调用,则调用) // (3) 如果父类没有,则继续找父类的父类, 整个规则,就是一样的,直到 Object 类 // 提示:如果查找属性的过程中,找到了,但是不能访问,则报错, cannot access // // 如果查找属性的过程中,没有找到,则提示属性不存在 System.out.println(n1); System.out.println(this.n1); // 找 n1 (super.n1) 的顺序是直接查找父类属性,其他的规则一样 System.out.println(super.n1); } // 访问父类的方法,不能访问父类的 private 方法 super.方法名(参数列表); public void ok() { super.test100(); super.test200(); super.test300(); // super.test400(); // 不能访问父类 private 方法 } public B() { // 访问父类的构造器(这点前面用过):super(参数列表); 只能放在构造器的第一句,只能出现一句! // super(); // super("jack", 10); super("jack"); } } ====================================================================================================== Super01.java: package src.com.hspedu.super_; public class Super01 { public static void main(String[] args) { B b = new B(); // 子类对象 // b.sum(); b.test(); } } //super.n1=999 //Base类的cal()方法...
-
-
this与super的区别
-
变量与方法 this super 访问局部变量 访问本类中的局部变量,如果局部变量名和类属性名相同则需要使用this来区分 从父类继承的属性前面无需使用super关键字 访问属性 访问本类中的属性变量,如果局部变量名和类属性名相同则需要使用this来区分 从父类继承的属性前面需要使用super关键字 访问局部变量 访问本类的构造器,必须放在构造器的首行 访问父类的构造器,也必须放在子类构造器的首行 构造器 构造当前对象 子类中访问父类的构造器
-
方法重写
-
介绍
- 方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
-
注意事项
-
子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样。
-
子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
比如父类返回类型是Object,子类方法返回类型是String -
子类方法不能缩小父类方法的访问权限【演示】 public> protected > 默认>private
-
Animal.java: package src.com.hspedu.override_; public class Animal { public void cry() { System.out.println("动物叫唤.."); } public Object m1() { return null; } public String m2() { return null; } public AAA m3() { return null; } protected void eat() { } } ========================================================================== Dog.java: package src.com.hspedu.override_; public class Dog extends Animal { // 老韩解读 // 1. 因为Dog 是 Animal子类 // 2. Dog 的 cry 方法和 Animal的 cry定义形式一样(名称、返回类型、参数) // 3. 这时我们就说 Dog的cry方法,重写了Animal的cry方法 public void cry() { System.out.println("小狗汪汪叫.."); } // 细节: 子类方法的返回类型和父类方法返回类型一样, // 或者是父类返回类型的子类 // 比如 父类 返回类型是 Object, // 子类方法返回类型是String public String m1() { return null; } // m2方法中Object是String的父类,所以需要调整逻辑或者注释说明 // public Object m2() { // return null; // 这里可以返回null,因为null可以表示任何类型 // } // m3方法的返回类型需要明确,如果BBB是AAA的子类,则应该有返回值 //BBB m3() { // return new BBB(); // 返回BBB的实例 // } // 细节:子类方法不能缩小父类方法的访问权限【演示】 // public>protected>默认>private // 吃的方法需要具体实现,这里默认为public public void eat() { System.out.println("小狗正在吃..."); } } class AAA { } class BBB extends AAA { } ========================================================================== Override01.java: package src.com.hspedu.override_; public class Override01 { public static void main(String[] args) { // 演示方法重写的情况 Dog dog = new Dog(); dog.cry(); // ctrl+b } } //小狗汪汪叫..
-
-
-
重写与重载的区别
-
名称 定义范围 方法名 形参列表 返回类型 备注 修饰符 重载(overload) 本类 必须一样 类型,个数,顺序至少有一个不同 无要求 无特殊 无要求 重写(override) 父子类 必须一样 形同 相同 子类重写的方法,返回值的类型和父类返回值的类型一致,或者是其子类 子类方法不能缩小父类方法的访问范围 -
代码示例
-
题目要求: 1. 编写一个Person类,包括属性/private(name、age),构造器、方法say(返回自我介绍的字符串)。 2. 编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法(返回自我介绍的信息)。 3. 在main中,分别创建Person和Student对象,调用say方法输出自我介绍 代码示例: Person.java: package src.com.hspedu.override_; //编写一个Person类,包括属性/private(name、age),构造器、方法say(返回自我介绍的字符串) public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String say() { return "name=" + name + " age=" + 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; } } =========================================================================================== Student.java: package src.com.hspedu.override_; //编写一个Student 类,继承Person类,增加id、score属性/private,以及构造器,定义say方法(返回自我介绍的信息)。 public class Student extends Person { private int id; private double score; public Student(String name, int age, int id, double score) { super(name, age);//这里会调用父类构造器 this.id = id; this.score = score; } //say public String say() { //这里体现 super 的一个好处,代码复用. return super.say() + " id=" + id + " score=" + score; } public int getId() { return id; } public void setId(int id) { this.id = id; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } } =========================================================================================== OverrideExercise.java: package src.com.hspedu.override_; public class OverrideExercise { public static void main(String[] args) { //在main中,分别创建Person和Student对象,调用say方法输出自我介绍 Person jack = new Person("jack", 10); System.out.println(jack.say()); Student smith = new Student("smith", 20, 123456, 99.8); System.out.println(smith.say()); } } //name=jack age=10 //name=smith age=20 id=123456 score=99.8
-
-
方法
==与equals
-
概念
-
代码示例
-
package com.hspedu.object_; public class EqualsExercise02 { public static void main(String[] args) { Person_ p1 = new Person_(); p1.name = "hspedu"; Person_ p2 = new Person_(); p2.name = "hspedu"; System.out.println(p1==p2); //False System.out.println(p1.name .equals( p2.name));//T System.out.println(p1.equals(p2));//False String s1 = new String("asdf"); String s2 = new String("asdf"); System.out.println(s1.equals(s2));//T System.out.println(s1==s2); //F } } class Person_{//类 public String name; }
-
hashcode
-
介绍
- 返回该对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。实际上,由object类定义的hashCode方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是Java=编程语言不需要这种实现技巧。)
-
特点
- 提高具有哈希结构的容器的效率!
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。
-
代码示例
-
public class hello { public static void main(String[] args) { AA aa = new AA(); AA aa2 = new AA(); AA aa3 = aa; System.out.println("aa.hashCode()=" + aa.hashCode()); System.out.println("aa2.hashCode()=" + aa2.hashCode()); System.out.println("aa3.hashCode()=" + aa3.hashCode()); } } class AA {} 输出结果: aa.hashCode()=990368553 aa2.hashCode()=1828972342 aa3.hashCode()=990368553
-
tostring
-
介绍
- 默认返回:全类名+@+哈希值的十六进制,【查看Object的toString方法】
- 子类往往重写toString方法,用于返回对象的属性信息
-
代码示例
-
public class hello { public static void main(String[] args) { /* Object的toString() 源码 (1)getClass().getName() 类的全类名(包名+类名 ) (2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */ Monster monster = new Monster("小妖怪", "巡山的", 1000); System.out.println(monster.toString() + " hashcode=" + monster.hashCode()); System.out.println("==当直接输出一个对象时,toString 方法会被默认的调用=="); System.out.println(monster); //等价 monster.toString() } } class Monster { private String name; private String job; private double sal; public Monster(String name, String job, double sal) { this.name = name; this.job = job; this.sal = sal; } //重写toString方法, 输出对象的属性 //使用快捷键即可 alt+insert -> toString @Override public String toString() { //重写后,一般是把对象的属性值输出,当然程序员也可以自己定制 return "Monster{" + "name='" + name + '\'' + ", job='" + job + '\'' + ", sal=" + sal + '}'; } @Override protected void finalize() throws Throwable { System.out.println("fin.."); } } 输出结果: Monster{name='小妖怪', job='巡山的', sal=1000.0} hashcode=1867083167 ==当直接输出一个对象时,toString 方法会被默认的调用== Monster{name='小妖怪', job='巡山的', sal=1000.0}
-
综合项目
-
零钱通
-
项目说明
- 使用Java 开发 零钱通项目 , 可以完成收益入账,消费,查看明细,退出系统等功能.
-
代码示例
-
首先我们现在一个文件,将所有功能简单的实现
-
package src.com.hspedu.smallchange; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class SmallChangeSys { //化繁为简 //1. 先完成显示菜单,并可以选择菜单,给出对应提示 //2. 完成零钱通明细 //3. 完成收益入账 //4. 消费 //5. 退出 //6. 用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n ,否则循环输入指令,直到输入y 或者 n //7. 在收益入账和消费时,判断金额是否合理,并给出相应的提示 public static void main(String[] args) { //定义相关的变量 boolean loop = true; Scanner scanner = new Scanner(System.in); String key = ""; //2. 完成零钱通明细 //老韩思路, (1) 可以把收益入账和消费,保存到数组 (2) 可以使用对象 (3) 简单的话可以使用String拼接 String details = "-----------------零钱通明细------------------"; //3. 完成收益入账 完成功能驱动程序员增加新的变化和代码 //老韩思路, 定义新的变量 double money = 0; double balance = 0; Date date = null; // date 是 java.util.Date 类型,表示日期 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); //可以用于日期格式化的 //4. 消费 //定义新变量,保存消费的原因 String note = ""; do { System.out.println("\n================零钱通菜单==============="); System.out.println("\t\t\t1 零钱通明细"); System.out.println("\t\t\t2 收益入账"); System.out.println("\t\t\t3 消费"); System.out.println("\t\t\t4 退 出"); System.out.print("请选择(1-4): "); key = scanner.next(); //使用switch 分支控制 switch (key) { case "1": System.out.println(details); break; case "2": System.out.print("收益入账金额:"); money = scanner.nextDouble(); //money 的值范围应该校验 -》 一会在完善 //老师思路, 编程思想 //找出不正确的金额条件,然后给出提示, 就直接break if(money <= 0) { System.out.println("收益入账金额 需要 大于 0"); break; } //找出正确金额的条件 balance += money; //拼接收益入账信息到 details date = new Date(); //获取当前日期 details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance; break; case "3": System.out.print("消费金额:"); money = scanner.nextDouble(); //money 的值范围应该校验 -》 一会在完善 //找出金额不正确的情况 //过关斩将 校验方式. if(money <= 0 || money > balance) { System.out.println("你的消费金额 应该在 0-" + balance); break; } System.out.print("消费说明:"); note = scanner.next(); balance -= money; //拼接消费信息到 details date = new Date(); //获取当前日期 details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance; break; case "4": //用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n , // 否则循环输入指令,直到输入y 或者 n // 老韩思路分析 // (1) 定义一个变量 choice, 接收用户的输入 // (2) 使用 while + break, 来处理接收到的输入时 y 或者 n // (3) 退出while后,再判断choice是y还是n ,就可以决定是否退出 // (4) 建议一段代码,完成一个小功能,尽量不要混在一起 String choice = ""; while (true) { //要求用户必须输入y/n ,否则就一直循环 System.out.println("你确定要退出吗? y/n"); choice = scanner.next(); if ("y".equals(choice) || "n".equals(choice)) { break; } //第二个方案 // if("y".equals(choice)) { // loop = false; // break; // } else if ("n".equals(choice)) { // break; // } } //当用户退出while ,进行判断 if (choice.equals("y")) { loop = false; } break; default: System.out.println("选择有误,请重新选择"); } } while (loop); System.out.println("-----退出了零钱通项目-----"); } }
-
-
将上面的代码转换为oop即可
-
oop编程: SmallChangeSysOOP.java: package src.com.hspedu.smallchange.oop; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; /** * 该类是完成零钱通的各个功能的类 * 使用OOP(面向对象编程) * 将各个功能对应一个方法. */ public class SmallChangeSysOOP { //属性.. //定义相关的变量 boolean loop = true; Scanner scanner = new Scanner(System.in); String key = ""; //2. 完成零钱通明细 //老韩思路, (1) 可以把收益入账和消费,保存到数组 (2) 可以使用对象 (3) 简单的话可以使用String拼接 String details = "-----------------零钱通明细------------------"; //3. 完成收益入账 完成功能驱动程序员增加新的变化和代码 //老韩思路, 定义新的变量 double money = 0; double balance = 0; Date date = null; // date 是 java.util.Date 类型,表示日期 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); //可以用于日期格式化的 //4. 消费 //定义新变量,保存消费的原因 String note = ""; //先完成显示菜单,并可以选择 public void mainMenu() { do { System.out.println("\n================零钱通菜单(OOP)==============="); System.out.println("\t\t\t1 零钱通明细"); System.out.println("\t\t\t2 收益入账"); System.out.println("\t\t\t3 消费"); System.out.println("\t\t\t4 退 出"); System.out.print("请选择(1-4): "); key = scanner.next(); //使用switch 分支控制 switch (key) { case "1": this.detail(); break; case "2": this.income(); break; case "3": this.pay(); break; case "4": this.exit(); break; default: System.out.println("选择有误,请重新选择"); } } while (loop); } //完成零钱通明细 public void detail() { System.out.println(details); } //完成收益入账 public void income() { System.out.print("收益入账金额:"); money = scanner.nextDouble(); //money 的值范围应该校验 -》 一会在完善 //老师思路, 编程思想 //找出不正确的金额条件,然后给出提示, 就直接return if(money <= 0) { System.out.println("收益入账金额 需要 大于 0"); return; //退出方法,不在执行后面的代码。 } //找出正确金额的条件 balance += money; //拼接收益入账信息到 details date = new Date(); //获取当前日期 details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance; } //消费 public void pay() { System.out.print("消费金额:"); money = scanner.nextDouble(); //money 的值范围应该校验 -》 一会在完善 //找出金额不正确的情况 //过关斩将 校验方式. if(money <= 0 || money > balance) { System.out.println("你的消费金额 应该在 0-" + balance); return; } System.out.print("消费说明:"); note = scanner.next(); balance -= money; //拼接消费信息到 details date = new Date(); //获取当前日期 details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance; } //退出 public void exit() { //用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n , // 否则循环输入指令,直到输入y 或者 n // 老韩思路分析 // (1) 定义一个变量 choice, 接收用户的输入 // (2) 使用 while + break, 来处理接收到的输入时 y 或者 n // (3) 退出while后,再判断choice是y还是n ,就可以决定是否退出 // (4) 建议一段代码,完成一个小功能,尽量不要混在一起 String choice = ""; while (true) { //要求用户必须输入y/n ,否则就一直循环 System.out.println("你确定要退出吗? y/n"); choice = scanner.next(); if ("y".equals(choice) || "n".equals(choice)) { break; } //第二个方案 // if("y".equals(choice)) { // loop = false; // break; // } else if ("n".equals(choice)) { // break; // } } //当用户退出while ,进行判断 if (choice.equals("y")) { loop = false; System.out.println("-----退出了零钱通项目-----"); } } } ===================================================================================================== SmallChangeSysApp.java: package src.com.hspedu.smallchange.oop; /** * 这里我们直接调用SmallChangeSysOOP 对象,显示主菜单即可 */ public class SmallChangeSysApp { public static void main(String[] args) { System.out.println("====hello公司===="); new SmallChangeSysOOP().mainMenu(); } }
-
-
-
房屋出租系统
-
项目需求
- 实现基于文本界面的《房屋出租软件》。 能够实现对房屋信息的添加、修改和删除(用数组实现),并能够打印房屋明细表
-
代码示例
-
文件结构: └─src └─com └─hspedu └─houserent ├─domain └─House.java ├─service └─HouseService.java ├─utils └─Utility.java └─view └─HouseView.java └─HouseRentApp.java House.java: package src.com.hspedu.houserent.domain; /** * House的对象表示一个房屋信息 */ public class House { //编号 房主 电话 地址 月租 状态(未出租/已出租) private int id; private String name; private String phone; private String address; private int rent; private String state; //构造器和setter,getter public House(int id, String name, String phone, String address, int rent, String state) { this.id = id; this.name = name; this.phone = phone; this.address = address; this.rent = rent; this.state = state; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getRent() { return rent; } public void setRent(int rent) { this.rent = rent; } public String getState() { return state; } public void setState(String state) { this.state = state; } //为了方便的输出对象信息,我们实现toString //编号 房主 电话 地址 月租 状态(未出租/已出租) @Override public String toString() { return id + "\t\t" + name + "\t" + phone + "\t\t" + address + "\t" + rent + "\t" + state ; } } ==================================================================================================================== HouseView.java: package src.com.hspedu.houserent.view; import src.com.hspedu.houserent.domain.House; import src.com.hspedu.houserent.service.HouseService; import src.com.hspedu.houserent.utils.Utility; /** * 1. 显示界面 * 2. 接收用户的输入 * 3. 调用HouseService完成对房屋信息的各种操作 */ public class HouseView { private boolean loop = true; //控制显示菜单 private char key = ' '; //接收用户选择 private HouseService houseService = new HouseService(2);//设置数组的大小为2 //根据id修改房屋信息 public void update() { System.out.println("=============修改房屋信息============"); System.out.println("请选择待修改房屋编号(-1表示退出)"); int updateId = Utility.readInt(); if (updateId == -1) { System.out.println("=============你放弃修改房屋信息============"); return; } //根据输入得到updateId,查找对象 //老师特别提示:返回的是引用类型[即:就是数组的元素] //因此老师在后面对house.setXxx() ,就会修改HouseService中houses数组的元素!!!!!!!!!! House house = houseService.findById(updateId); if (house == null) { System.out.println("=============修改房屋信息编号不存在..============"); return; } System.out.print("姓名(" + house.getName() + "): "); String name = Utility.readString(8, "");//这里如果用户直接回车表示不修改该信息,默认"" if (!"".equals(name)) {//修改 house.setName(name); } System.out.print("电话(" + house.getPhone() + "):"); String phone = Utility.readString(12, ""); if (!"".equals(phone)) { house.setPhone(phone); } System.out.print("地址(" + house.getAddress() + "): "); String address = Utility.readString(18, ""); if (!"".equals(address)) { house.setAddress(address); } System.out.print("租金(" + house.getRent() + "):"); int rent = Utility.readInt(-1); if (rent != -1) { house.setRent(rent); } System.out.print("状态(" + house.getState() + "):"); String state = Utility.readString(3, ""); if (!"".equals(state)) { house.setState(state); } System.out.println("=============修改房屋信息成功============"); } //根据id查找房屋信息 public void findHouse() { System.out.println("=============查找房屋信息============"); System.out.print("请输入要查找的id: "); int findId = Utility.readInt(); //调用方法 House house = houseService.findById(findId); if (house != null) { System.out.println(house); } else { System.out.println("=============查找房屋信息id不存在============"); } } //完成退出确认 public void exit() { //这里我们使用Utility提供方法,完成确认 char c = Utility.readConfirmSelection(); if (c == 'Y') { loop = false; } } //编写delHouse() 接收输入的id,调用Service 的del方法 public void delHouse() { System.out.println("=============删除房屋信息============"); System.out.print("请输入待删除房屋的编号(-1退出):"); int delId = Utility.readInt(); if (delId == -1) { System.out.println("=============放弃删除房屋信息============"); return; } //注意该方法本身就有循环判断的逻辑,必须输出Y/N char choice = Utility.readConfirmSelection(); if (choice == 'Y') {//真的删除 if (houseService.del(delId)) { System.out.println("=============删除房屋信息成功============"); } else { System.out.println("=============房屋编号不存在,删除失败============"); } } else { System.out.println("=============放弃删除房屋信息============"); } } //编写addHouse() 接收输入,创建House对象,调用add方法 public void addHouse() { System.out.println("=============添加房屋============"); System.out.print("姓名: "); String name = Utility.readString(8); System.out.print("电话: "); String phone = Utility.readString(12); System.out.print("地址: "); String address = Utility.readString(16); System.out.print("月租: "); int rent = Utility.readInt(); System.out.print("状态: "); String state = Utility.readString(3); //创建一个新的House对象, 注意id 是系统分配的, House newHouse = new House(0, name, phone, address, rent, state); if (houseService.add(newHouse)) { System.out.println("=============添加房屋成功============"); } else { System.out.println("=============添加房屋失败============"); } } //编写listHouses()显示房屋列表 public void listHouses() { System.out.println("=============房屋列表============"); System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(未出租/已出租)"); House[] houses = houseService.list();//得到所有房屋信息 for (int i = 0; i < houses.length; i++) {//大家想想,这里老韩有什么?雷,坑 if (houses[i] == null) {//如果为null,就不用再显示了 break; } System.out.println(houses[i]); } System.out.println("=============房屋列表显示完毕============"); } //显示主菜单 public void mainMenu() { do { System.out.println("\n=============房屋出租系统菜单============"); System.out.println("\t\t\t1 新 增 房 源"); System.out.println("\t\t\t2 查 找 房 屋"); System.out.println("\t\t\t3 删 除 房 屋 信 息"); System.out.println("\t\t\t4 修 改 房 屋 信 息"); System.out.println("\t\t\t5 房 屋 列 表"); System.out.println("\t\t\t6 退 出"); System.out.print("请输入你的选择(1-6): "); key = Utility.readChar(); switch (key) { case '1': addHouse(); break; case '2': findHouse(); break; case '3': delHouse(); break; case '4': update(); break; case '5': listHouses(); break; case '6': exit(); break; } } while (loop); } } ==================================================================================================================== HouseService.java: package src.com.hspedu.houserent.service; import src.com.hspedu.houserent.domain.House; /** * HouseService.java<=>类 [业务层] * //定义House[] ,保存House对象 * 1. 响应HouseView的调用 * 2. 完成对房屋信息的各种操作(增删改查c[create]r[read]u[update]d[delete]) */ public class HouseService { private House[] houses; //保存House对象 private int houseNums = 1; //记录当前有多少个房屋信息 private int idCounter = 1; //记录当前的id增长到哪个值 //构造器 public HouseService(int size) { //new houses houses = new House[size];//当创建HouseService对象,指定数组大小 //为了配合测试列表信息,老韩这里初始化一个House对象 houses[0] = new House(1,"jack","112", "海淀区", 2000, "未出租"); } //findById方法,返回House对象或者null public House findById(int findId) { //遍历数组 for(int i = 0; i < houseNums; i++) { if(findId == houses[i].getId()) { return houses[i]; } } return null; } //del方法,删除一个房屋信息 public boolean del(int delId) { //应当先找到要删除的房屋信息对应的下标 //老韩强调,一定要搞清楚 下标和房屋的编号不是一回事 int index = -1; for(int i = 0; i < houseNums; i++) { if(delId == houses[i].getId()) {//要删除的房屋(id),是数组下标为i的元素 index = i;//就使用index记录i } } if(index == -1) {//说明delId在数组中不存在(有点绕..) return false; } //如果找到, 这里需要小伙伴动脑筋,有点难. //老师思路分析: for(int i = index; i < houseNums - 1; i++) { houses[i] = houses[i+1]; } //把当有存在的房屋信息的最后一个 设置null houses[--houseNums] = null; return true; } //add方法,添加新对象,返回boolean public boolean add(House newHouse) { //判断是否还可以继续添加(我们暂时不考虑数组扩容的问题) => 能否自己加入数组扩容机制(~~) if(houseNums == houses.length) {//不能再添加 System.out.println("数组已满,不能再添加了..."); return false; } //把newHouse对象加入到,新增加了一个房屋 houses[houseNums++] = newHouse; //我们程序员需要设计一个id自增长的机制, 然后更新newHouse的id newHouse.setId(++idCounter); return true; } //list方法,返回houses public House[] list() { return houses; } } ==================================================================================================================== HouseRentApp.java: package src.com.hspedu.houserent; import src.com.hspedu.houserent.view.HouseView; public class HouseRentApp { public static void main(String[] args) { byte b1 = 123; b1 += 100000; //如果 b1 = b1 + 100000;//编译都不能过 System.out.println(b1); byte b2 = 123; b2 = (byte)(b2 + 100000); System.out.println(b2); //创建HouseView对象,并且显示主菜单,是整个程序的入口 new HouseView().mainMenu(); System.out.println("===你退出房屋出租系统=="); } }
-