title: JavaSE基础实验
toc: true
categories:
abbrlink: c6d5a8a9
date: 2021-02-06 17:21:25
tags: Java
top:
这里是记录 JavaSE 基础实验的报告
[TOC]
(一)初识Java
实验 1 文件操作实验
实验项目
- 文件操作实验
实验需求
- 文件操作
实验内容
- 创建空文件
touch Demo.java
- 创建含内容的文件
通过>>符号将控制台输入信息重定向到Demo.java文件中,输入EOF结束命令
cat >> Demo.java << EOF
lanqiao
java
EOF
-
显示文件
- cat:由第一行开始显示文件内容
- more:一页一页地显示文件内容
- less:与more类似,但是比more更好的是,可以往前翻页
- tail:只看结尾几行
cat Demo.java
more Demo.java
less Demo.java
tail Demo.java
- 拷贝文件
- 将当前目录下的
Demo.java
文件拷贝到Public
目录下,文件名为Demo1.java
cp Demo.java /home/shiyanlou/Public/Demo1.java
- 移动文件
将/home/shiyanlou/Public/Demo1.java
文件移动到当前目录,并重新命名为Demo2.java
mv /home/shiyanlou/Public/Demo1.java Demo2.java
- 显示目录或文件
- 简单显示当前目录下的文件或目录,不能显示隐藏目录或文件
ls
详细显示目录或文件的属性信息
ls -li
显示隐藏文件或目录,隐藏文件或目录以.开头,例如:.bash_profile
ls -a
- 文件压缩
- 把当前目录下的Demo.java、Demo2.java两个文件压缩为Demo.tar
tar -cf Demo.tar Demo.java Demo2.java
- 删除文件
rm Demo.java
rm Demo2.java
- 文件解压
tar -xvf Demo.tar
- 删除多余文件
rm Demo.tar
rm Demo2.java
实验总结
- Java语言的特性之一是跨平台特性,即一次编写处处运行(Once Write,Anywhere Run)。考虑到Java程序开发阶段和运行阶段可能在不同操作系统上进行,本节实验以Ubuntu为例,简要讲解文件的操作,为后续搭建Java运行环境提供技术支撑。
实验 2 目录操作实验
实验项目
- 目录操作实验
实验需求
- 目录操作
实验内容
- 创建目录
在当前目录下创建单个目录,如目录为lanqiao
mkdir lanqiao
copy
- 创建多级目录
创建多级目录,例如:java/jdk8
,表示创建java目录和该目录下的子目录jdk8
mkdir -p java/jdk8
copy
- 删除目录
删除前面创建的目录lanqiao
,注意,该命令要求目录下不为空
rmdir lanqiao
copy
- 删除多级目录
删除前面创建的多级目录java,通过添加参数-p来控制递归删除
rmdir -p java/jdk8
copy
- 目录切换
目录可以分为当前目录和绝对目录。当前目录以.开头;绝对目录以/开头;上级目录以..表示;用户目录以~表示。
切换到根目录
cd /
copy
切换到指定目录
cd /home/shiyanlou
copy
切换到上级目录
cd ..
copy
切换到用户目录
cd ~
copy
- 显示目录
显示当前目录
pwd
实验 3 Java开发环境搭建
实验项目
- Java开发环境搭建
实验需求
- 下载安装JDK
- 配置环境变量
实验内容
- 下载JDK
在实验环境桌面打开一个浏览器,输入网址: http://www.oracle.com/technetwork/java/javase/downloads/index.html这里选择JDK版本,您可以按照自己的需求选择。点击JDK下方的JDK Download下载,如下图所示:
点击“Download”后,将会进入如下图所示页面,此处要注意两点:
1)需要登录Oracle账号,如果没有账号请提前注册。
2)根据自己的系统和需求,选择合适的版本。比如本实验环境,是Ubuntu系统,并且是64位的,所以选择“Linux-x64”。
下载完毕后,打开文件所在文件夹
- 安装JDK
输入命令进行解压,为了避免手动输入JDK文件名错误而产生不必要的麻烦,我们可以先输入ls
查看一下当前目录下的文件,然后鼠标右键复制粘贴文件名。
解压完毕后如下图所示:
- 配置环境变量
要对profile
进行配置:
输入命令:
sudo vim /etc/profile
1)添加JAVA_HOME
路径:
export JAVA_HOME=/xxx/xxxx/jdk1.8.0_251/
该目录是你JDK解压后的目录,以本实验环境为例,解压后的目录为:
/home/shiyanlou/Downloads/jdk1.8.0_251/
所以本实验环境的路径为:
export JAVA_HOME=/home/shiyanlou/Downloads/jdk1.8.0_251/
2)添加JRE
路径
本实验环境的为:
export JRE_HOME=/home/shiyanlou/Downloads/jdk1.8.0_251/jre/
3)配置CLASSPATH路径
export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
4)配置PATH路径
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
添加完成后按esc
,再输入:wq
保存退出
输入命令 :
source /etc/profile
让刚才的配置生效
4.验证
输入命令:
java -version
可以看到版本号,安装成功!
实验总结
- 要想编译、运行Java程序,首先要搭建Java开发环境。本次实验就讲解如何搭建Java开发环境。
实验 4 “HelloWorld”实验
实验项目
- “HelloWorld”实验
实验需求
- 编写helloworld程序
实验内容
- 新建一个Java文件
在webide
左边空白处,鼠标右键选择New File
新建一个文件,给文件取名HelloWorld.java
,如下图所示:
- 编写代码
Java源文件以java为扩展名。源文件的基本组成部分是类(class),如本例中的HelloWorld类。一个源文件中最多只能有一个public类,其他类的个数不限,如果源文件包含一个public类,则该源文件必须以public类名命名。
1)编写程序结构:
public class HelloWorld{
}
程序的基本组成部分是类,这里命名为HelloWorld,因为前面有public(公共的)修饰,所以程序源文件的名称必须和类名一致。类名后面有一对大括号,所有属于这个类的代码都写在这对大括号里面。
2)编写main方法:
public static void main(String[] args){
}
一个程序运行起来需要有个入口,main()方法就是这个程序的入口,是这个程序运行的起始点。需要注意的是,一个程序只能有一个main()方法,否则java虚拟机不知道从哪个main()方法开始运行。编写main()方法时,按照上门的格式和内容书写即可,内容不能缺少,顺序也不能调整,对具体的各个修饰符的作用,将会在后面的课程中详细介绍。
3)编写执行代码:
System.out.println("HelloWorld!");
System.out.println("********")
方法的作用很简单,就是向控制台输出********
,输出之后自动换行。
完整代码如下:
public class HelloWorld{
public static void main(String[] args){
System.out.println("HelloWorld!");
}
}
实验结果
实验总结
- 使用 public 修饰的 class 名(HelloWorld)须和源代码文件名相同
- Java 中所有的代码都必须包含在 class 中
- main() 方法是程序的入口,每个程序有且只有一个main()方法
- Java是区分大小写的
- Java程序由一条条语句构成,每个语句以;结束
实验 5 Javac、Java命令实验
实验项目
- Javac、Java命令实验
实验需求
- 编译运行HelloWorld程序
实验内容
javac
命令
javac
命令是将源代码编译成class
字节码文件,因为我们的JVM
虚拟机是执行class
字节码文件的,不是执行源代码,JVM
虚拟机是不认识源代码的。
1)首先我们需要回顾一下上一个实验,在webide
里面新建一个HelloWorld.java
文件,然后写一个”HelloWorld“
程序。
2)在webide
下方控制台输入命令:
javac HelloWorld.java
然后回车执行,此时你会发现,左侧列表里生成了一个 .class 扩展名的文件,那么说明编译成功,反之编译失败。
- java命令
java命令用于运行Java程序,它会启动Java虚拟机,Java虚拟机加载相关的类,然后调用主程序main()方法。
此时再输入如下命令:
java HelloWorld
然后回车执行:
程序经过编译后,完美运行。
实验结果
(二)Java基础语法
实验 6 变量和常量实验
实验项目
- 变量和常量实验
实验需求
- 操作变量和常量
实验内容
- 变量
在 Java 中,变量需要先声明 (declare) 才能使用。在声明中,说明变量的类型,赋予变量以特别名字,以便在后面的程序中调用它。你可以在程序中的任意位置声明变量,语法格式如下:
数据类型 变量名称;
例如:
int a = 1;
在该语法格式中,数据类型可以是 Java 语言中任意的类型,如 int。变量名称是该变量的标识符,需要符合标识符的命名规则,数据类型和变量名称之间使用空格进行间隔,使用 ; 作为结束。变量标识符的命名规范如下:
- 首字符必须是字母、下划线(―)、美元符号($)或者人民币符号(¥)。
- 标识符由数字(09)、大写字母(AZ)、小写字母(a~z)、下划线(―)、美元符号($)、人民币符号(¥)以及所有在十六进制 0xc0 前的 ASCII 码组成。
- 不能把关键字、保留字作为标识符。
- 标识符的长度没有限制。
- 标识符区分大小写。
初始化变量是指为变量指定一个明确的初始值。初始化变量有两种方式:一种是声明时直接赋值,一种是先声明、后赋值。如下代码分别使用两种方式对变量进行了初始化。
char usersex='女'; // 直接赋值
或者:
String username; // 先声明
username ="琪琪"; // 后赋值
另外,多个同类型的变量可以同时定义或者初始化,但是多个变量中间要使用逗号分隔,声明结束时用分号分隔。如下:
String username,address,phone,tel; // 声明多个变量
int num1=12,num2=23,result=35; // 声明并初始化多个变量
[copy]
Java 中初始化变量时需要注意以下事项:
- 变量是类或者结构中的字段,如果没有显式地初始化,默认状态下创建变量并默认初始值为 0。
- 方法中的变量必须显式地初始化,否则在使用该变量时就会出错。
接下来,我们一起来实践一下。在 /home/project/
新建一个 VarTest.java
文件:
public class VarTest{
public static void main(String[] args){
System.out.println("This is a test for var");
int a; //声明变量a
a = 5;
System.out.println(a); // 打印一个整数a
}
}
编译运行:
$ javac VarTest.java
$ java VarTest
这是一个声明变量实验
5
- 常量
Java 中的 final 关键字可以用于声明属性(常量),方法和类。当 final 修饰属性时,代表该属性一旦被分配内存空间就必须初始化,它的含义是“这是无法改变的”或者“终态的”。在变量前面添加关键字 final 即可声明一个常量。在 Java 编码规范中,要求常量名必须大写。
语法格式:
final 数据类型 常量名 = 值;
例如:
final double PI = 3.14;
常量也可以先声明,再进行赋值,但只能赋值一次,比如:
final int FINAL_VARIABLE;
FINAL_VARIABLE = 100;
在定义常量时,需要注意如下内容:
- 在定义常量时就需要对该常量进行初始化。
- final 关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法。
- 为了与变量区别,常量取名一般都用大写字符。
当常量被设定后,一般情况下不允许再进行更改,如果更改其值将提示错误。
在/home/project/
下新建一个FinalVar.java
:
public class FinalVar{
public static void main(String[] args){
final String FINAL_STRING="lanqiao";
System.out.println(FINAL_STRING);
}
}
实验结果
实验总结
- 在使用变量时,要避免出现未赋值就使用的情况。虽然在后面的实验中,会看到一些变量即使不赋值也会有默认值,但作为程序员,为了避免程序出错,也要做到变量先赋值后使用。
实验 7 整型类型实验
实验项目
- 整型类型实验
实验需求
- 操作整型类型
实验内容
- Java 定义了 4 种整数类型变量:字节型(byte)、短整型(short)、整型(int)和长整型(long)。这些都是有符号的值,正数或负数。`
byte是8位数据类型,占用1个字节,默认值是0,取值范围(-2^7) ~ (2^7-1),也就是 -128 ~ 127之间,所以最大存储数据量是255;
byte num1 = 10; byte num2=-10;`
short是16位的数据类型,占用2个字节,默认值是0,取值范围(-2^15) ~(2^15-1),也就是 -32768 ~ 32767之间,所以最大数据存储量是65536;
short num1=100; short num2=-200;
int是32位的数据类型,占用4个字节,默认值是0,取值范围(-2^31) ~(2^31-1),也就是 -2147483648 ~ 2147483647之间,所以最大数据存储量是2^32-1;
int num1=1000; int num2=-2000;
long是64位的数据类型,占用8个字节,默认值是0L,取值范围(-2^63) ~(2^63-1),也就是 -9223372036854775808 ~ 9223372036854775808之间,所以最大数据存储量是2^64;
long num1=10000L; long num2=-20000L;
在 /home/shiyanlou/ 下新建一个文件 Number.java
public class Number {
public static void main(String[] args) {
byte a = 20; // 声明一个byte类型的变量并赋予初始值为20
short b = 10; // 声明一个short类型的变量并赋予初始值为10
int c = 30; // 声明一个int类型的变量并赋予初始值为30
long d = 40; // 声明一个long类型的变量并赋予初始值为40
long sum = a + b + c + d;
System.out.println("20+10+30+40=" + sum);
}
}
编译运行:
$ javac Number.java
$ java Number
20+10+30+40=100
在该示例中,首先依次定义了 byte 类型、short 类型、int 类型和 long 类型的 4 个变量,并赋予了初始值,然后定义了一个 long 类型、名称为 sum 的变量。sum 变量的值为前 4 个变量之和,最后输出 sum 变量的值,即相加之后的结果。
提示:因为 byte 类型、short 类型、int 类型和 long 类型都是整数类型,故可以使用“+”相加,而非字符串之间的连接
实验结果
1
实验总结
- Java各整数类型有固定的表示范围和字段长度,其不受具体操作系统的影响,以保证Java程序的可移植性。
- Java语言设计出4种整型类型的目的是存不同大小的数,这样可以节约存储空间,对于一些硬件内存小或者要求运行速度快的系统显得尤为重要。
实验 8 浮点类型实验
实验项目
- 浮点类型实验
实验需求
- 操作浮点类型
实验内容
- 浮点类型是带有小数部分的数据类型,也叫实型。浮点型数据包括单精度浮点型(float)和双精度浮点型(double),代表有小数精度要求的数字。
float是32位的数据类型,占用4个字节,默认值是0,取值范围3.4e-45 ~ 1.4e38 之间;
float num1=10.25f; float num2=-20.35F;
double是64位的数据类型,占用8个字节,默认值是0,取值范围4.9e-324 ~ 1.8e308 之间;
double num1=10.123d; double num2= -10.25644D;
注意:一个值要能被真正看作 float,它必须以 f(或 F)后缓结束;否则,会被当作 double 值。对 double 值来说,d(或 D)后缓是可选的。
不同于整型,通过简单的推算,程序员就可以知道这个类型的整数的取值范围。对于float和double,要想推算出来,需要理解浮点型的存储原理,且计算起来比较复杂。接下来通过下面的程序,可以直接再控制台输出这两种类型的最小值和最大值。
在 /home/shiyanlou/ 下新建一个文件 FloatDouble.java
class FloatDouble{
public static void main(String[] args){
System.out.println("float最小值="+Float.MIN_VALUE);
System.out.println("float最大值="+Float.MAX_VALUE);
System.out.println("double最小值="+Double.MIN_VALUE);
System.out.println("double最大值="+Double.MAX_VALUE);
}
}
实验结果
实验总结
- 在计算机系统的发展过程中,曾经提出过多种表示实数的方法,但是到目前为止使用最广泛的是浮点表示法。相对于定点数而言,浮点数利用指数使小数点的位置可以根据需要而上下浮动,从而可以灵活地表达更大范围的实数。
- 单精度浮点型(float)和双精度浮点型(double)之间的区别主要是所占用的内存大小不同,float 类型占用 4 字节的内存空间,double 类型占用 8 字节的内存空间。双精度类型 double 比单精度类型 float 具有更高的精度和更大的表示范围。
实验 9 字符型类型
实验项目
- 字符型类型 实验
实验需求
- 操作字符型类型
实验内容
- char是字符类型,占用2个字节,默认值为空,取值范围 为 0~65535,也就是 u0000 ~ uffff。
char a=1; char b='A';
Java字符型常量有以下3种表示形式。
1)用英文单引号括起来得单个字符,例如’a’、’汉’。
2)用英文单引号括起来的十六进制字符代码值来表示单个字符,其格式为’uXXXX’,其中u是约定的前缀(u是Unicode的第一个字母),而后面的XXXX位是4位十六进制数,是该字符再Unicode字符集中的序号,例如’u0061’。
3)某些特殊的字符可以采用转义符”来表示,将其后面的字符转变为其他的含义,例如’t’ 代表制表符,’n’代表换行符,’t’代表回车符等。
通过下面的程序及程序的运行结果,可以进一步了解Java字符的使用方法。
在 /home/shiyanlou/ 下新建一个文件 CharShow.java
public class CharShow{
public static void main(String[] args){
char char1 = 'q';
char char2 = '桥';
System.out.println("显示汉字:"+char2);
char char3 = 'u0061';
System.out.println("Unicode代码0061代表的字符为:"+char3);
char char4 = 't';
System.out.println(char4+"Unicode代码0061代表的字符为:"+char3);
}
}
实验结果
实验总结
- 字符型(char型)数据用来表示通常意义上的字符。
- 字符常量为用单引号括起来的单个字符,因为java使用Unicode编码,一个Unicode编码占2个字节,一个汉字也是占2个字节,所以在java中字符型变量可以存放一个汉字。
实验 10 布尔类型实验
实验项目
- 布尔类型实验
实验需求
- 操作布尔类型数据
实验内容
- 布尔类型(boolean)用于对两个数值通过逻辑运算,判断结果是“真”还是“假”。Java 中用保留字 true 和 false 来代表逻辑运算中的“真”和“假”。因此,一个 boolean 类型的变量或表达式只能是取 true 和 false 这两个值中的一个。
在 Java 语言中,布尔类型的值不能转换成任何数据类型,true 常量不等于 1,而 false 常量也不等于 0。这两个值只能赋给声明为 boolean 类型的变量,或者用于布尔运算表达式中。
例如,可以使用以下语句声明 boolean 类型的变量。
boolean isable; // 声明 boolean 类型的变量 isable
boolean b = false; // 声明 boolean 类型的变量 b,并赋予初值为 false
接下来,我们一起来使用一下boolean类型,在 /home/shiyanlou/ 下新建一个文件 BooleanShow.java
public class BooleanShow {
public static void main(String[] args) {
boolean b;
b = false;
System.out.println("b is " + b);
b = true;
System.out.println("b is " + b);
}
}
实验结果
实验总结
- Java中boolean类型可以表示真或假,只允许取值true或false(不可以用0或非0的整数替代true和false,这点和C语言不同)。
- boolean类型适用于逻辑运算,一般用于程序流程控制,后面流程控制的课程经常会使用到布尔型。
实验 11 基本类型转换实验
实验项目
- 基本类型转换实验
实验需求
- 基本类型转换
实验内容
- 整型、常量、字符型数据可以混合运算。运算中,不同类型的数据先转化为 同一类型,然后进行运算
自动转换时有规律的,小的类型自动转换为大的类型;整数类型可以自动转换为浮点类型,但可能会产生舍入误差,字符也可以自动提升为整数
类型转换包含着强制类型转换,强制类型转换可能会导致溢出或损失精度,要注意的是,浮点数是通过舍弃小数得到的,而不是四舍五入。 通过下面的程序及程序的运行结果,可以进一步加深对java基本数据类型转换的认识。
在 /home/shiyanlou/ 下新建一个文件 TestConvert.java
public class TestConvert {
public static void main(String[] args) {
int i1 = 222;
int i2 = 333;
double d1 = (i1 + i2) * 2.9; // 系统将转换为double型运算
float f1 = (float) ((i1 + i2) * 2.9); // 从double型转换成float型,需要进行强制类型转换
System.out.println(d1);
System.out.println(f1);
byte b1 = 88;
byte b2 = 99;
byte b3 = (byte) (b1 + b2); // 系统先转换为int型运算,再从int型转换成byte型
// 需要进行强制类型转换
System.out.println("88+99=" + b3); // 强制类型转换,数据结果溢出
double d2 = 5.1E88;
float f2 = (float) d2; // 从double型强制转换成float型,结果溢出
System.out.println(f2);
float f3 = 3.14F;
f3 = f3 + 0.05F; // 这条语句不能写成f3=f3+0.5; 否则会报错,因为0.05是double型
// 加上f3,仍然是double型,赋给float会报错
System.out.println("3.14F+0.05F=" + f3);
}
}
实验结果
实验总结
- Java数据类型转换分为一下3种:基本数据类型转换、字符串与其他数据类型转换、其他实用数据类型转换。此处介绍Java基本数据类型转换,其中boolean类型不可以和其他数据类型互相转换。
- 容量小的类型自动转换成容量大的数据类型,如上图
- byte、short、char 之间不会互相转换,三者在计算是首先会转换为int类型
- 容量大的数据类型换成容量小的数据类型时,需要加上强制转换符,但可能造成精度降低或溢出,使用时需要格外注意
- 有多种类型的数据混合运算时,系统首先自动地转换成容量最大的数据类型,然后再进行计算
实验 12 变量作用范围实验
实验项目
- 变量作用范围实验
实验需求
- 变量作用范围
实验内容
- 变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。
成员变量
Java 的成员变量有两种,分别是全局变量和静态变量(类变量)。定义在方法体和语句块之外,不属于任何一个方法,作用域是整个类。
假设在一个类中声明了 4 个变量,下面编写一个测试类输出引起变量的值改变的示例代码。
变量声明代码如下所示:
public class DataClass {
String name; // 成员变量、实例变量
int age; // 成员变量、实例变量
static final String website = "蓝桥官网"; // 成员变量、静态变量(类变量)
static String URL = "https://www.lanqiao.cn/"; // 成员变量、静态变量(类变量)
}
测试类代码如下所示:
public class Test {
public static void main(String[] args) {
// 创建类的对象
DataClass dc = new DataClass();
// 对象名.变量名调用实例变量(全局变量)
System.out.println(dc.name);
System.out.println(dc.age);
// 对象名.变量名调用静态变量(类变量)
System.out.println(dc.website);
System.out.println(dc.URL);
// 类名.变量名调用静态变量(类变量)
System.out.println(DataClass.website);
System.out.println(DataClass.URL);
}
}
局部变量
局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:
方法参数变量(形参):在整个方法内有效。
方法局部变量(方法内定义): 从定义这个变量开始到方法结束这一段时间内有效。
代码块局部变量(代码块内定义):从定义这个变量开始到代码块结束这一段时间内有效。
注意:局部变量在使用前必须被程序员主动初始化值。
下面,我们通过代码来实践一下,新建一个文件 VarScope.java
public class VarScope{
static float varQ=9.1F; //成员变量,其作用域从变量定义位置起至类结束
{
int varB=10; //语句块中的局部变量,其作用域从变量定义位置起至语句块结束
System.out.println("varB="+varB); //可以使用本语句块中的局部变量varB
System.out.println("varQ="+varQ); //可以使用成员变量varQ
}
public static void main(String[] args){
int varL = 8; //方法中的局部变量,其作用域从变量定义位置起至方法结束
System.out.println("varL="+varL); //可以使用本方法中的局部变量varL
System.out.println("varQ="+varQ); //可以使用成员变量varQ
//System.out.println("varB="+varB); //不可以使用其他方法(或语句块)中的局部变量
}
float varT=varQ+1.0F; //可以使用成员变量varQ,varT本身也是成员变量
}
编译运行:
$ javac VarScope.java
$ java VarScope
varL=8
varQ=9.1
实验结果
实验总结
- 根据变量声明位置的不同,可以将变量分为成员变量和局部变量。
- 方法中的局部变量可以和方法外的成员变量同名。在使用的时候,如果在局部变量所在的方法体内,局部变量覆盖成员变量,输出的结果是局部变量的值。如果在局部变量所在的方法外,不在局部变量的作用域内,输出的是成员变量的值。
实验 13 累加累减实验
实验项目
- 累加累减实验
实验需求
- 累加累减操作
实验内容
-
自增 (++) 和自减 (–) 运算符有两种写法:前缀(++i,–i)和后缀(i++,i–)。
前缀自增自减法 (++i,–i): 先进行自增或者自减运算,再进行表达式运算。
后缀自增自减法 (i++,i–): 先进行表达式运算,再进行自增或者自减运算
我们直接通过代码来深入学习。
在 /home/shiyanlou/ 下新建一个文件 ArithmeticOpr.java
public class ArithmeticOpr {
public static void main(String[] args) {
int i1 = 10, i2 = 20;
int i = (i2++); // ++在i2后,故先运算(赋值)再自增
System.out.print("i=" + i);
System.out.println(" i2=" + i2);
i = (++i2); // ++在i2前,故先自增再运算(赋值)
System.out.print("i=" + i);
System.out.println(" i2=" + i2);
i = (--i1); // --在i1前,故先自减再运算(赋值)
System.out.print("i=" + i);
System.out.println(" i1=" + i1);
i = (i1--); // --在i1后,故先运算(赋值)再自减
System.out.print("i=" + i);
System.out.println(" i1=" + i1);
}
}
编译运行:
$ javac ArithmeticOpr.java
$ java ArithmeticOpr
i=20 i2=21
i=22 i2=22
i=9 i1=9
i=9 i1=8
实验结果
实验总结
- 通过这个实验可以看出,++和–这两个运算符放在操作数前或后,决定这是先自增(或自减)再运算,还是先运算再自增(或自减);注意 : 自增或自减在操作数前, 则优先运算自增或自减 ; 在后, 则自增或自减在最后运算 ;
- 自增自减运算符存在于C/C++/C###/Java/Python等高级语言中,它的作用是在运算结束前(前置自增自减运算符)或后(后置自增自减运算符)将变量的值加(或减)一。
- 相较于这些语言中的+=和-=运算符,自增运算符更加简洁,且可以控制效果作用于运算之前还是之后,具有很大的便利性。
实验 14 短路与或非实验
实验项目
- 短路与或非实验
实验需求
- 验证短路与或非
实验内容
- 单个的逻辑运算符会将左右两个表达式都进行运算得出布尔值,再进行运算。
‘短路与’若左边表达式为false则不会对右边的表达式进行判断,因为结果必为false;
‘短路或’若左边表达式结果为true则不会对右边的表达式进行判断,因为结果必为true。
在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
int a = 1;
System.out.println(false & a++ == 1);// 即使前面为false,右边的表达式还是会继续执行,即num++
System.out.println(a);// 2
int b = 1;
System.out.println(false && b++ == 1);// 左边的表达式为false,右边的表达式将不会执行,即num不会自增
System.out.println(b);// 1
}
}
编译运行:
$ javac Test.java
$ java Test
false
2
false
1
短路与是同样的道理,大家可以自己编写代码验证一下
实验结果
1.
实验总结
- 短路运算符就是我们常用的“&&”、“||”,一般称为“条件操作”。
- 短路运算符只能用于逻辑表达式内,非短路运算符可用于位表达式和逻辑表达式内。也可以说:短路运算只能操作布尔型的,而非短路运算不仅可以操作布尔型,而且可以操作数值型。
实验 15 三目运算符执行顺序实验
实验项目
- 三目运算符执行顺序实验
实验需求
- 验证三目运算符执行顺序
实验内容
- 三目运算符
Java 提供了一个特别的三元运算符(也叫三目运算符)经常用于取代某个类型的 if-else 语句。条件运算符的符号表示为?“:,使用该运算符时需要有三个操作数,因此称其为三目运算符。使用条件运算符的一般语法结构为:
(表达式1)?(表达式2):(表达式3)
其中,表达式1是一个布尔表达式。当 表达式1为真时,执行 表达式2, 否则就执行 表达式3。
下面是一个使用三目运算符的示例:
int x;
int y;
int z;
x = 6;
y = 2;
z = x > y ? x - y : x + y;
在这里要计算 z 的值,首先要判断 x>y 表达的值,如果为 true,z 的值为 x-y;否则 z 的值为 x+y。很明显 x>y 表达式结果为 true,所以 z 的值为 4。
三目运算符优先级
在使用条件运算符时,还应该注意优先级问题,例如下面的表达式:
x>y ? x-=y : x+=y;
在编译时会出现语法错误,因为条件运算符优先于赋值运算符,上面的语句实际等价于:
(x>y ? x-=y : x)+=y;
而运算符“+=”是赋值运算符,该运算符要求左操作数应该是一个变量,因此出现错误。为了避免这类错误,可以使用括号“( )”来加以区分。例如,下面是正确的表达式。
(x>y) ? (x-=y): (x+=y);
我们通过下面的代码来更加深入了解三目运算符的执行顺序。
在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
int i = 10;
int j = 20;
int result = i++ == 10 ? i++ : j++;
// (表达式1)?(表达式2):(表达式3)
// 先判断表达式1是否成立,成立即将表达式2的值赋给result,否则将表达式3的值赋给result
System.out.println(" result=" + result); // 执行表达式1之前i还没自加,所以结果为表达式2的值
System.out.println(" i=" + i); // 执行完表达式1和表达式2后i自加了2次,所以是12
System.out.println(" j=" + j); // j++没有执行过,所以还是20
}
}
编译运行:
$ javac Test.java
$ java Test
result=11
i=12
j=20
实验结果
实验总结
- 在Java程序中,需要进行大量的计算,所以要使用到运算符号,下面先来给大家说明Java三目运算符如何运用
- 所有的数学运算都认为是从左向右运算的,Java 语言中大部分运算符也是从左向右结合的,只有单目运算符、赋值运算符和三目运算符例外,其中,单目运算符、赋值运算符和三目运算符是从右向左结合的,也就是从右向左运算。
实验 16 位运算实现变量交换实验
实验项目
- 位运算实现变量交换实验
实验需求
- 位运算实现变量交换
实验内容
- 位逻辑运算符包含 4 个:&(与)、|(或)、~(非)和 ^(异或)。除了 ~(即位取反)为单目运算符外,其余都为双目运算符。本实验仅对^进行讲解。
对于数值的交换操作,通常会采用第三个变量作为中间变量过渡的交换方式。
int a = 129;
int b = 128;
int temp = a;
a = b;
b = temp;
使用这种方法,可以保证交换的准确性,但是由于会使用第三个变量,占用了额外的内存空间。那么有没有办法不借助第三个变量实现两个数值的交换呢?
使用异或运算可以避免申请额外变量,并且位运算的速度也更快:
a = a ^ b;
b = a ^ b;
a = a ^ b;
这个交换两个变量而无需借助第三个临时变量过程,其实现主要是基于异或运算的如下性质:
1)任意一个变量X与其自身进行异或运算,结果为0,即X^X=0
2)任意一个变量X与0进行异或运算,结果不变,即X^0=X
3)异或运算具有可结合性,即a^b^c=(a^b)^c=a^(b^c)
4)异或运算具有可交换性,即a^b=b^a 分析:
第一步: a = a ^ b;
完成后 a变量的结果为a ^ b
第二步: b = a ^ b;
此时赋值号右边的a保存的是a ^ b的值,那么将赋值号右边的a用a ^ b替换,
得到(a ^ b) ^ b=a ^ (b ^ b)=a ^0=a,
即经过第二步运算后b中的值为a,即b=a,将a换到了b里
第三步: a = a ^ b;
此时赋值号右边的a保存的仍然是a ^ b的值,不变,而赋值号右边的b已经是a 了,
将赋值号右边的a,b分别进行替换,
即此时赋值号右边a ^ b=(a ^ b)^ a=a ^ b^ a=a ^ a^ b=0^ b=b, 该值赋值给a,即a=b
即经过第三步运算后a中的值为b,即a=b,将b换到了a里
这样经过如上的三步骤,完成了交换两个变量a,b而无需借助第3个临时变量过程。
这个过程等价于如下的过程,:
a=a+b
b=a-b;
a=a-b;
前提是a+b的值不能溢出。
我们通过代码测试一下:
在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
int a = 129;
int b = 128;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("a=" + a); // 输出为128
System.out.println("b=" + b); // 输出为129
}
}
编译运行:
$ javac Test.java
$ java Test
a=128
b=129
实验结果
实验总结
-
位操作是程序设计中对位模式按位或二进制数的一元和二元操作。
-
在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多。
-
在现代架构中, 情况并非如此:位运算的运算速度通常与加法运算相同(仍然快于乘法运算)。
-
位运算是对字节或字中的实际二进制位进行检测、设置或移位,它只适用于字符型和整数型变量以及它们的变体,对其它数据类型不适用。
-
关系运算和逻辑运算表达式的结果只能是1 或0,而位运算的结果可以取0 或1 以外的值。
-
要注意区别位运算符和逻辑运算符的不同。
实验 17 简单科学计算器实验
实验项目
- 简单科学计算器实验
实验需求
- 实现一个简单科学计算器
- 让用户输入两个数,最后显示 计算结果
实验内容
- 首先,我们要在控制台能输入数字1和数字2,这样才能计算。所以我们需要一个Scanner(扫描器)获取用户的输入。下面是创建Scanner对象的基本语法:
Scanner s = new Scanner(System.in);
接下来我们演示一个最简单的数据输入:
Scanner input = new Scanner(System.in);
System.out.print("请输入第一个数:");
int num1 = input.nextInt();
这样num1的值就是我们在控制台输入的值。具体原理会在后面的实验中讲解,大家现在会使用即可。
- 获取两个数后,我们就可以利用算术运算符对它们进行运算。
result1 = num1 + num2;
最后我们输出运算结果即可。
我们一起来实践一下,在 /home/shiyanlou/ 下新建一个文件 Calculator.java
import java.util.Scanner; //导入 java.util 包下的 Scanner 类,导入后才能bai使用它
public class Calculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入第一个数:");
double num1 = input.nextDouble();
System.out.print("请输入第二个数:");
double num2 = input.nextDouble();
double result1 = num1 + num2;
double result2 = num1 - num2;
double result3 = num1 * num2;
double result4 = num1 / num2;
System.out.println(num1 + "+" + num2 + "=" + result1);
System.out.println(num1 + "-" + num2 + "=" + result2);
System.out.println(num1 + "*" + num2 + "=" + result3);
System.out.println(num1 + "/" + num2 + "=" + result4);
}
}
再编译运行
实验结果
实验 18 表达式优先级实验
实验项目
- 表达式优先级实验
实验需求
- 验证表达式优先级
实验内容
- 使用优先级为 1 的小括号可以改变其他运算符的优先级,即如果需要将具有较低优先级的运算符先运算,则可以使用小括号将该运算符和操作符括起来。例如下面的表达式:
(x-y)*z/5
在这个表达式中先进行括号内的减法运算,再将结果与 z 相乘,最后将积除以 5 得出结果。整个表达式的顺序按照从左向右执行,比较容易理解。
再来看一个复杂的表达式,如下所示。
--y || ++x && ++z;
这个表达式中包含了算术运算符和逻辑运算符。根据表 1 中列出的优先级,可以确定它的执行顺序如下: 1)先计算 y 的自减运算符,即 –y。 2)再计算 x 的自增运算符,即 ++x。 3)接着计算 z 的自增运算符,即 ++z。 4)由于逻辑与比逻辑或的优先级高,这里将步骤2和步骤3的结果进行逻辑与运算,即 ++x && ++z。 5)最后将 步骤4的结果与步骤1 进行逻辑或运算,即 –y||++x&&++z。
如果没有上述对该表达式执行顺序的说明,第一眼看到它时将很难识别优先级。对于这类问题,可以通过添加小括号使表达的顺序更加清晰,而不用去查优先级表。如下所示为改进后的表达式。
(--y)||((++x)&&(++z));
技巧:记住这么多运算符的优先级是比较困难的,因此大家应该在实际应用中多多练习。
我们通过代码来深入了解表达式优先级,在 /home/shiyanlou/ 下新建一个文件 Test.java
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
int appleNum = 0; // 苹果数
int stuNum = -1; // 小朋友数
double stuApple = -1; // 每个小朋友得到多少苹果
Scanner input = new Scanner(System.in);
System.out.println("请输入篮子里有几个苹果:");
appleNum = input.nextInt();
System.out.println("请输入屋子里有几个小朋友:");
stuNum = input.nextInt();
stuApple = appleNum / stuNum;
System.out.println("每个小朋友得到:" + stuApple + "个苹果");
}
}
编译运行:
$ javac Test.java
$ java Test
请输入篮子里有几个苹果:3
请输入屋子里有几个小朋友:6
每个小朋友得到:0.0个苹果
如上结果所示,得到的结果并不是我们预期的结果。原因在于stuApple=AppleNum/stuNum这条语句,首先运算的是appleNum/stuNum,之后再进行赋值运算。appleNum/stuNum这个表达式中的两个操作数都是int型的,其运算结果也是int型,所以出现了3除以6,得到int型0的情况,再将int型的0赋给double类型的stuApple,结果显示出0.0。
实验结果
实验总结
- Java 语言中运算符的优先级共分为 14 级,其中 1 级最高,14 级最低。在同一个表达式中运算符优先级
高的先执行。下表列出了所有的运算符的优先级以及结合性。
-
运算符的优先级是帮助我们在一个表达式中如何对于不同的运算符和相同的运算符,进行正确的运算顺序。
-
运算符的优先级不需要特别地去记忆它,比较复杂的表达式一般使用圆括号 () 分开,提高可读性。
(三)流程控制
实验 19 多重if判断实验
实验项目
- 多重if判断实验
实验需求
- 使用多重if判断
实验内容
if 语句的主要功能是给程序提供一个分支。然而,有时候程序中仅仅多一个分支是远远不够的,甚至有时候程序的分支会很复杂,这就需要使用多分支的 if…else if 语句。
通常表现为“如果满足某种条件,就进行某种处理,否则如果满足另一种条件才执行另一种处理……,这些条件都不满足则执行最后一种条件”。
if…else if 多分支语句的语法格式如下所示:
if(表达式1) {
语句块1;
} else if(表达式2) {
语句块2;
...
} else if(表达式n) {
语句块n;
} else {
语句块n+1;
}
可以看出,else-if 结构实际上是 if-else 结构的多层嵌套。明显的特点就是在多个分支中只执行一个语句组,而其他分支都不执行,所以这种结构可以用于有多种判断结果的分支中。
在使用 if…else if 语句时,依次判断表达式的值,当某个分支的条件表达式的值为 true 时,则执行该分支对应的语句块,然后跳到整个 if 语句之外继续执行程序。如果所有的表达式均为 false,则执行语句块 n+1,然后继续执行后续程序,其运行流程图如下:
多重if语句
注意:如果 if(或 else if,或 else) 条件成立时的执行语句只有一条,是可以省略大括号的!但如果执行语句有多条,那么大括号就是不可或缺的。
我们通过代码来学习,在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
int score = 85;
if (score > 90) {
System.out.println("奖励一个iphone100");
} else if (score > 70) {
System.out.println("奖励一个小米");
} else {
System.out.println("罚俯卧撑100个");
}
}
}
编译运行:
$ javac Test.java
$ java Test
奖励一个小米
实验结果
实验总结
-
Java支持两种选择语句:if 语句和 switch 语句。其中 if 语句使用布尔表达式或布尔值作为分支条件来进行分支控制,而 switch 语句则用于对多个整型值进行匹配,从而实现分支控制。这些语句允许你只有在程序运行时才能知道其状态的情况下,控制程序的执行过程。
-
这种结构是从上到下逐个对条件进行判断,一旦发现条件满足就执行与该条件相关的语句,并跳过其他的条件判断;若没有一个条件满足,则执行最后一个else后的语句块;如果没有最后的else语句,则不执行任何操作,执行该结构后面的语句。同样,语句块中包含多条语句时,必须使用“{”和“}”把多条语句括起来。
-
实验 20 switch分支判断实验
实验项目
- switch分支判断实验
实验需求
- 练习switch分支判断
实验内容
switch 语句是 Java 的多路分支语句。它提供了一种基于一个表达式的值来使程序执行不同部分的简单方法。因此,它提供了一个比一系列 if-else-if 语句更好的选择。
语法:
switch(表达式){
case 值1:
代码块1
break;
case 值2:
代码块2
break;
...
default:
默认执行的代码块
}
其中,switch、case、default、break 都是 Java 的关键字。
- switch 表示“开关”,这个开关就是 switch 关键字后面小括号里的值,小括号里要放一个整型变量或字符型变量。表达式必须为 byte,short,int,char类型。
Java7 增强了 switch 语句的功能,允许 switch 语句的控制表达式是 java.lang.String 类型的变量或表达式。只能是 java.lang.String 类型,不能是 StringBuffer 或 StringBuilder 这两种字符串的类型。
- case 表示“情况,情形”,case 标签可以是:
-
类型为 char、byte、 short 或 int 的常量表达式
-
枚举常量
-
从 Java SE 7 开始, case 标签还可以是字符串字面量
- default
表示“默认”,即其他情况都不满足。default 后要紧跟冒号,default 块和 case 块的先后顺序可以变动,不会影响程序执行结果。通常,default 块放在末尾,也可以省略不写。
- break
表示“停止”,即跳出当前结构。
如果在 case 分支语句的末尾没有 break 语句,有可能触发多个 case 分支。那么就会接着执行下一个 case 分支语句。这种情况相当危险,常常会引发错误。
我们通过代码来学习一下,新建一个源代码文件TestSwitch.java。
public class TestSwitch {
public static void main(String[] args){
int num = 2;
switch(num){
case 1:
System.out.println("恭喜你,获得了一等奖");
break;
case 2:
System.out.println("恭喜你,获得了二等奖");
break;
case 3:
System.out.println("恭喜你,获得了三等奖");
break;
default:
System.out.println("很遗憾,下次再来");
}
}
}
编译运行:
$ javac TestSwitch.java
$ java TestSwitch
恭喜你,获得了二等奖
实验结果
实验总结
-
当需要对选项进行等值判断时,使用 switch 语句更加简洁明了。比如:摇号摇到 1 的得一等奖,摇到 2 的得二等奖,摇到 3 的等三等奖,摇到其他的没有奖。
-
switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
-
switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
-
case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
-
当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
-
当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
-
switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
-
数字缩写用num表示;
-
-
switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
实验 21 switch-case下坠实验
实验项目
- switch-case下坠实验
实验需求
- 练习switch-case下坠
实验内容
switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。
- 如果 case 语句块中没有 break 语句时,JVM 并不会顺序输出每一个 case 对应的返回值,而是继续匹配,匹配不成功则返回默认 case。
如下代码:
public class Test {
public static void main(String args[]){
int i = 5;
switch(i){
case 0:
System.out.println("0");
case 1:
System.out.println("1");
case 2:
System.out.println("2");
default:
System.out.println("default");
}
}
}
编译运行:
$ javac Test.java
$ java Test
default
- 如果 case 语句块中没有 break 语句时,匹配成功后,从当前 case 开始,后续所有 case 的值都会输出。如下代码:
public class Test2 { public static void main(String args[]){ int i = 1; switch(i){ case 0: System.out.println("0"); case 1: System.out.println("1"); case 2: System.out.println("2"); default: System.out.println("default"); } } }
编译运行:
$ javac Test2.java
$ java Test2
1
2
default
- 如果当前匹配成功的 case 语句块没有 break 语句,则从当前 case 开始,后续所有 case 的值都会输出,如果后续的 case 语句块有 break 语句则会跳出判断。如下代码:
public class Test3 { public static void main(String args[]){ int i = 1; switch(i){ case 0: System.out.println("0"); case 1: System.out.println("1"); case 2: System.out.println("2"); case 3: System.out.println("3"); break; default: System.out.println("default"); } } }
编译运行:
$ javac Test3.java
$ java Test3
1
2
3
我们通过代码实践一下,新建一个源代码文件TestSwitch2.java。
public class TestSwitch {
public static void main(String[] args) {
int num = 1;
switch (num) {
case 1:
case 2:
System.out.println("恭喜你,获得了三亚七日游");
break;
case 3:
case 4:
System.out.println("恭喜你,获得了一辆奔驰");
break;
default:
System.out.println("很遗憾,下次再来");
}
}
}
运行发现
如果num是1或者2,则会输出“恭喜你,获得了三亚七日游”
如果num是3或者4,则会输出“恭喜你,获得了一辆奔驰”
实验结果
实验总结
- 如果当前匹配成功的 case 语句块没有 break 语句,则从当前 case 开始,后续所有 case 的值都会输出,如果后续的 case 语句块有 break 语句则会跳出判断
实验 22 do-while和while实验
实验项目
- do-while和while实验
实验需求
- 练习do-while和while
实验内容
while语法:
while(条件){
代码块
}
while 的执行过程是先判断,再执行。
判断 while 后面的条件是否成立 ( true or false )
当条件成立时,执行循环内的代码。
然后重复执行 1、2, 直到循环条件不成立为止。
while的语句流程
do-while 语法:
do{
代码块
}while(条件);
do-while 的执行过程是先执行一次,再循环判断(所以循环内的代码至少会执行一次)。
先执行一遍循环操作,然后判断循环条件是否成立。
如果条件成立,继续执行1、2,直到循环条件不成立为止。
do…while的流程
如:
int i = 0;
while(i < 100){
System.out.println("I love ShiYanlou!");
i++;
}
int i = 0;
do {
System.out.println("I love ShiYanlou!");
i++;
} while(i < 100);
练习:分别用 while 和 do-while 两种方法,编写代码,文件名为: SumOfEven.java。实现计算 1-1000 中所有偶数的和,并输出。验证一下两种方法你输出的结果是一致吗?
参考代码如下:
public class SumOfEven {
public static void main(String[] args){
int i1 = 1, i2 = 1;
int sum1 = 0, sum2 = 0;
while (i1 <= 1000){ //循环1000次
if(0 == i1 % 2){ //判断是否为偶数
sum1 += i1; //将偶数加入到总数里
}
i1++; //i自增1
}
System.out.println("用while,1到1000中,所有偶数的和为:"+sum1);
do {
if (0 == i2 % 2){ //在条件语句中,将数值写在前面是为了防止将==写成了=
sum2 += i2;
}
i2++;
} while(i2 <= 1000);
System.out.println("用do-while,1到1000中,所有偶数的和为:"+sum2);
}
}
编译运行:
$ javac SumOfEven.java
$ java SumOfEven
用while,1到1000中,所有偶数的和为:250500
用do-while,1到1000中,所有偶数的和为:250500
实验结果
实验总结
-
while是计算机的一种基本循环模式。当满足条件时进入循环,进入循环后,当条件不满足时,跳出循环。
-
do…while 循环是 while 循环的变体。在检查while()条件是否为真之前,该循环首先会执行一次do{}之内的语句,然后在while()内检查条件是否为真,如果条件为真的话,就会重复do…while这个循环,直至while()为假。
-
在使用while循环及do…while循环时必须注意,在循环体中要改变循环条件中的参数(例如本例中的i++)或者有其他跳出循环的语句,这样才能跳出循环,否则就出现死循环。
-
实验 23 for循环实验
实验项目
- for循环实验
实验需求
- 练习for循环
实验内容
for 语法:
for(循环变量初始化①; 循环条件②; 循环变量值操作③){
循环操作④
}
for的流程
例如,计算 100 以内不能被 3 整除的数之和:
int sum = 0; // 保存不能被3整除的数之和
// 循环变量 i 初始值为 1 ,每执行一次对变量加 1,只要小于等于 100 就重复执行循环
for (int i = 1;i<=100;i++) {
// 变量 i 与 3 进行求模(取余),如果不等于 0 ,则表示不能被 3 整除
if (i % 3 != 0) {
sum = sum + i; // 累加求和
}
}
System.out.println("1到100之间不能被3整除的数之和为:" + sum);
练习:编写代码,文件名为: SumOfEven.java,实现计算 1-1000 中所有偶数的和,并输出。
参考代码如下:
public class SumOfEven {
public static void main(String[] args){
int sum = 0;
for(int i = 1; i <= 1000; i++){
if(0 == i % 2){
sum += i;
}
}
System.out.println("用for,1到1000中,所有偶数和为:"+sum);
}
}
编译运行:
$ javac SumOfEven.java
$ java SumOfEven
用for,1到1000中,所有偶数和为:250500
实验结果
- 上个实验介绍了while循环和do…while循环,其实程序员在编程过程中,使用最多的循环结构是for循环。for循环主要的特点是结构清晰,易于理解,在解决能确定循环次数的问题时,首选for循环。
实验总结
for 相比 while 和 do-while 语句结构更加简洁易读,它的执行顺序:
-
执行循环变量初始化部分(1),设置循环的初始状态,此部分在整个循环中只执行一次。
-
进行循环条件的判断(2),如果条件为 true,则执行循环体内代码(4);如果为 false ,则直接退出循环。
-
执行循环变量值操作部分(3),对循环变量的值进行修改,然后进行下一次循环条件判断(4)。
-
实验 24 双重for循环实验
实验项目
- 双重for循环实验
实验需求
- 练习双重for循环
实验内容
********
********
********
********
********
********
********
********
********
********
现有一需求,如上所示。我们需要在控制台输出一个10行8列的矩形。我们已经学过for循环,有同学很快会想到利用一个for循环把第一行****循环输出10次不就行了吗?是的,这样我们很轻易的就会得到如上图形。但是我们每次只能输出一个*该怎么办?
思路:
- 首先上面提到,我们可以把****循环输出10次就可以了。但是我们每次只能输出一个。那么我们是不是可以先用循环输出8次可以得到****。
代码如下:
for (int i = 0; i < 8; i++) {
System.out.print("*");
}
这样是不是就可以得到****。
- 那我们得到了****,是不是把它循环10次就可以得到上面的矩形了?
代码如下:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 8; j++) {
System.out.print("*");
}
System.out.println(); //每一行输出完了换行
}
这就是双重for循环。
双重for循环的重点在于,内循环的循环条件往往和外循环的循环参数有关,例如下面例子中内循环的循环条件为j<=i,其中i时外循环的循环参数。
新建一个源代码文件TestFor.java。
public class TestFor {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) { // 循环10次
for (int j = 1; j <= i; j++) { // 每次输出当次个*
System.out.print("*");
}
System.out.println();
}
}
}
编译运行:
$ javac TestFor.java
$ java TestFor
*
**
***
****
*****
实验结果
实验总结
- 在前面介绍if语句的时候,提到了嵌套的if语句。同样,在for循环中,也可以嵌套for循环,如果只循环一次,就构成双重for循环。
- 实际上,嵌套循环不仅可以是两层嵌套,而且可以是三层嵌套、四层嵌套 …… 不论循环如何嵌套, 总可以把内层循环当成外层循环的循环体来对待,区别只是这个循环体里包含了需要反复执行的代码。
实验 25 continue和break实验
实验项目
- continue和break实验
实验需求
- 练习continue和break
实验内容
continue
continue 语句是跳过循环体中剩余的语句而强制执行下一次循环,其作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。
continue 语句类似于 break 语句,但它只能出现在循环体中。它与 break 语句的区别在于:continue 并不是中断循环语句,而是中止当前迭代的循环,进入下一次的迭代。简单来讲,continue 是忽略循环语句的当次循环。
注意:continue 语句只能用在 while 语句、for 语句或者 foreach 语句的循环体之中,在这之外的任何地方使用它都会引起语法错误。
下面看一个示例,代码如下:
for (int i = 1; i <= 10; i++) {
if (i == 3) { // 当i=3时
continue;// 通过continue结束本次循环
}
System.out.println(i);
}
编译运行:
1
2
4
5
6
7
8
9
10
在上述程序代码中,当条件 i==3 的时候执行 continue 语句,continue 语句会终止本次循环,循环体中 continue 之后的语句将不再执行,接着进行下次循环,所以输出结果中没有 3。
break
break 语句能用于任何 Java 循环中,包括人们有意设置的无限循环。在一系列嵌套循环中使用 break 语句时,它将仅仅终止最里面的循环。例如:
for (int i = 1; i <= 10; i++) {
if (i == 5) { // 当i=5时
break;// 通过continue结束本次循环
}
System.out.println(i);
}
编译运行:
1
2
3
4
你会发现当i=5时整个循环都结束了。
我们通过代码来实践一下,新建一个源代码文件Jump.java。
public class Jump{
public static void main(String[] args){
//break 练习
for(int i = 1; i <= 10; i++){
System.out.println("循环第"+i+"次");
if(0 == i % 3){
break;
}
if(0 == i % 5){
System.out.println("我进来了!");
}
}
//continue练习 打印10以内的所有奇数
for(int i = 1; i <= 10; i++){
if(0 == i % 2) //判断i是否为偶数
continue; //通过continue结束本次循环
System.out.println(i);
}
}
}
编译运行:
$ javac Jump.java
$ java Jump
循环第1次
循环第2次
循环第3次
1
3
5
7
9
实验结果
实验总结
- 在前面使用双重for循环时,一般会用到continue语句,continue语句的主要作用为跳出档次循环,继续执行下一次循环。其中break、continue以及后面要学的return语句,都是让程序从一部分跳转到另一部分,习惯上都成为跳转语句。
-
break 关键字经常用在条件和循环语句中,用来跳出循环语句。
-
continue关键字的作用是跳过循环体中剩余的语句执行下一次循环。
1.
实验 26 打印九九乘法口诀
实验项目
- 打印九九乘法口诀
实验需求
- 打印出九九乘法口诀
实验内容
思路:
- 我们首先思考一下,九九乘法表是个什么样子的,它是不是应该是个直角三角形的形状,如下所示:
* ** *** **** *****
我们学过双重for循环,是可以很轻易打印出如上图形的,代码如下:
for (int i = 1; i <= 5; i++) {
for (int j = 0; j < i; j++) {
System.out.print("*");
}
System.out.println(); //每行输出完换行
}
- 那既然得到了直角三角形,我们把*换成如下格式就可以了。
1*1=1 2*1=2 2*2=4 3*1=3 3*2=6 3*3=9 ......
代码如下:
// 外层循环控制行数
for (int i = 1; i <= 9; i++) {
// 内层循环控制每行表达式个数
for (int j = 1; j <= i; j++) {
System.out.print("" + i + "*" + j + "=" + (i * j)+" ");
}
// 一行结束换行
System.out.println();
}
我们一起动手来实践一下,新建一个源代码文件Test.java。
public class Test {
public static void main(String[] args) {
// 外层循环控制行数
for (int i = 1; i <= 9; i++) {
// 内层循环控制每行表达式个数
for (int j = 1; j <= i; j++) {
System.out.print("" + i + "*" + j + "=" + (i * j)+" ");
}
// 一行结束换行
System.out.println();
}
}
}
实验结果
实验总结
-
观察九九乘法口诀表,可以得出图表的规律:总共有9行,第几行就有几个表达式。每行表达式的规律:第j行,表达式就从ji开始,一直到jj结束,共有j个表达式。
-
-
for中的初始化是放在外面好还是放在里面好?
实验 27 简单科学计算器升级
实验项目
- 简单科学计算器升级
实验需求
- 让用户输入两个数,然后输入操作符最后显示 计算结果
实验内容
实验思路:
-
首先,我们要在控制台能输入两个操作数,代码如下:
Scanner input = new Scanner(System.in); System.out.print("请输入第一个数:"); double num1 = input.nextDouble(); System.out.print("请输入第二个数:"); double num2 = input.nextDouble();
-
其次,我们输入一个操作符(+、=、*、/):
System.out.print("请输入一个操作符(+、-、*、/):"); char choose = input.next().charAt(0);
-
然后进行对应的运算,该如何进行运算呢?这个时候我们就要用到switch分支了,选择操作符然后进行对应的运算。
switch (choose) { case '+': result = num1 + num2; System.out.println(num1 + "+" + num2 + "=" + result); break; case '-': result = num1 - num2; System.out.println(num1 + "-" + num2 + "=" + result); break; case '*': result = num1 * num2; System.out.println(num1 + "*" + num2 + "=" + result); break; case '/': result = num1 / num2; System.out.println(num1 + "/" + num2 + "=" + result); break;
有没有觉得很简单呢?
完整代码如下:
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入第一个数:");
double num1 = input.nextDouble();
System.out.print("请输入第二个数:");
double num2 = input.nextDouble();
System.out.print("请输入一个操作符(+、-、*、/):");
char choose = input.next().charAt(0);
double result = 0;
switch (choose) {
case '+':
result = num1 + num2;
System.out.println(num1 + "+" + num2 + "=" + result);
break;
case '-':
result = num1 - num2;
System.out.println(num1 + "-" + num2 + "=" + result);
break;
case '*':
result = num1 * num2;
System.out.println(num1 + "*" + num2 + "=" + result);
break;
case '/':
result = num1 / num2;
System.out.println(num1 + "/" + num2 + "=" + result);
break;
default:
System.out.println("您输入有误!");
break;
}
}
}
实验结果
实验总结
-
前面我们做了一个简单的计算器,但是无法输入加减乘除符号,现在我们学完了流程控制,就可以实现它了。
-
什么事情越早做越好,随着时间的流逝而更加有价值?
-
-
这个云主机上安装的jdk不是1.8版本的;
(四)方法与数组
实验 28 方法声明实验
实验项目
- 方法声明实验
实验需求
- 方法声明
实验内容
- 一个完整的方法通常包括方法名称、方法主体、方法参数和方法返回值类型其结构如下图所示:
成员方法一旦被定义,便可以在程序中多次调用,提高了编程效率。声明成员方法的语法格式如下:
[访问修饰符] 返回值类型 方法名([参数列表]) {
方法体
}
比如:
public class Test {
[public|private|protected][static]<void|return_type><method_name>([paramList]) {
// 方法体
}
}
注意:上述语法中,中括号“[]”中的部分表示可以省略,竖线“|”表示“或”,例如 public|private,说明可以使用 public 或 private 关键字,但是两个关键字不能同时出现。
上述代码中一个方法包含 4 部分:方法的返回值、方法名称、方法的参数和方法体。其中 retum_type 是方法返回值的数据类型,数据类型可以是原始的数据类型,即常用的 8 种数据类型,也可以是一个引用数据类型,如一个类、接口和数组等。
除了这些,一个方法还可以没有返回值,即返回类型为 void,像 main() 方法。method_name 表示自定义的方法名称,方法的名称首先要遵循标识符的命名约定,除此之外,方法的名称第一个单词的第一个字母是小写,第二单词的第一个字母是大写,依此类推。
paramList 表示参数列表,这些变量都要有自己的数据类型,可以是原始数据类型,也可以是复杂数据类型,一个方法主要依靠参数来传递消息。方法主体是方法中执行功能操作的语句。其他各修饰符的含义如下。
public、private、protected:表示成员方法的访问权限。
static:表示限定该成员方法为静态方法。
final:表示限定该成员方法不能被重写或重载。
abstract:表示限定该成员方法为抽象方法。抽象方法不提供具体的实现,并且所属类型必须为抽象类。
在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
method1();
method2();
method3(1, 2);
method4(3, 4);
}
// 无参无返回值方法
public static void method1() {
}
// 无参带返回值方法
public static int method2() {
int x = 10;
return x;
}
// 带参无返回值方法
public static void method3(int x, int y) {
}
// 带参带返回值方法
public static int method4(int x, int y) {
return x + y;
}
}
实验总结
- 方法是Java中一个命名的代码块,如同在数学中用到的函数,在其他语言中常直接称为函数。
-
根据方法是否带参、是否带返回值,可将方法分为四类:
无参无返回值方法
无参带返回值方法
带参无返回值方法
带参带返回值方法
实验 29 方法调用实验
实验项目
- 方法调用实验
实验需求
- 调用方法
实验内容
一般来说,可以通过以下方式来调用成员方法:
methodName({paramList})
关于方法的参数,经常会提到形参与实参,形参是定义方法时参数列表中出现的参数,实参是调用方法时为方法传递的参数。
下面 retumMin() 方法中的 m 和 n 是形参,调用 retumMin() 方法时的 x 和 y 是实参。
public int returnMin(int m,int n) {
return Math.min(m,n); // m和n是形参
}
public static void main(String[] args) {
int x = 50;
int y = 100;
Test t = new Test();
int i = t.returnMin(x,y); // x和y是实参
System.out.println(i);
}
方法的形参和实参具有以下特点:
形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在方法内部有效,方法调用结束返回主调方法后则不能再使用该形参变量。
实参可以是常量、变量、表达式、方法等,无论实参是何种类型的量,在进行方法调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使实参获得确定值。
实参和形参在数量、类型和顺序上应严格一致,否则会发生“类型不匹配” 的错误。
方法调用中发生的数据传送是单向的,即只能把实参的值传送绐形参,而不能把形参的值反向地传送给实参。因此在方法调用过程中,形参的值发生改变,而实参中的值不会变化。
下面的示例演示了调用 add() 方法前后形参 x 的变化。
public int add(int x) {
x += 30;
System.out.println("形参 x 的值:"+x);
return x;
}
public static void main(String[] args) {
int x = 150;
System.out.println("调用 add() 方法之前 x 的值:"+x);
Test t = new Test();
int i = t.add(x);
System.out.println("实参 x 的值:"+x);
System.out.println("调用 add() 方法的返回值:"+i);
}
运行上述程序,输出结果如下:
调用 add() 方法之前 x 的值:150
形参 x 的值:180
实参 x 的值:150
调用 add() 方法的返回值:180
从输出结果可以看出,形参 x 值的改变,并没有影响实参 x。
在调用成员方法时应注意以下 4 点:
- 对无参成员方法来说,是没有实际参数列表的(即没有 paramList),但方法名后的括号不能省略。
- 对带参数的成员方法来说,实参的个数、顺序以及它们的数据类型必须与形式参数的个数、顺序以及它们的数据类型保持一致,各个实参间用逗号分隔。实参名与形参名可以相同,也可以不同。
- 实参也可以是表达式,此时一定要注意使表达式的数据类型与形参的数据类型相同,或者使表达式的类型按 Java 类型转换规则达到形参指明的数据类型。
- 实参变量对形参变量的数据传递是“值传递”,即只能由实参传递给形参,而不能由形参传递给实参。程序中执行到调用成员方法时,Java 把实参值复制到一个临时的存储区(栈)中,形参的任何修改都在栈中进行,当退出该成员方法时,Java 自动清除栈中的内容。
另外,在方法体内可以定义本方法所使用的变量,这种变量是局部变量。它的生存期与作用域是在本方法内,也就是说,局部变量只能在本方法内有效或可见,离开本方法则这些变量将被自动释放。
在方法体内定义变量时,变量前不能加修饰符。局部变量在使用前必须明确赋值,否则编译时会出错。另外,在一个方法内部,可以在复合语句(把多个语句用括号{}括起来组成的一个语句称复合语句)中定义变量,这些变量只在复合语句中有效。
实验结果
实验总结
- 当方法定义好之后,需要调用才可以生效,我们可以通过 main 方法(main 方法是 Java 程序的入口,所以需要用它来调用)来调用它
- 本次实验只介绍类内部方法的调用,关于调用其他类的方法,在后面面向对象的课程中会学习到。在类内部调用方法很简单,只需给出方法名以及方法的实际参数列表(实参列表的数据类型必须与形参列表一致或可以自动转换成形参列表的格式)即可。
实验 30 一维数组声明
实验项目
- 一维数组声明
实验需求
- 声明一维数组
实验内容
创建一维数组
数组的定义
数组中的元素都可以通过下标来访问,下标从 0 开始,到数组长度 -1 结束。例如,可以通过 ages[0] 获取数组中的第一个元素 18 ,ages[3] 就可以取到第四个元素 10。
注意:
使用数组前要声明数组。
语法:
数据类型[ ] 数组名; //或者: 数据类型 数组名[ ];
以上两种格式都可以声明一个数组,其中的数据类型既可以是基本数据类型,也可以是引用数据类型。数组名可以是任意合法的变量名。声明数组就是要告诉计算机该数组中数据的类型是什么。例如:
int[] score; // 存储学生的成绩,类型为整型
double[] price; // 存储商品的价格,类型为浮点型
String[] name; // 存储商品名称,类型为字符串型
在声明数组时不需要规定数组的长度,例如:
int score[10]; // 这是错误的
注意:在声明数组变量时千万不要漏写[]。
分配空间
声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。
简单地说,分配空间就是要告诉计算机在内存中为它分配几个连续的位置来存储数据。在 Java 中可以使用 new 关键字来给数组分配空间。分配空间的语法格式如下:
arrayName = new type[size]; // 数组名 = new 数据类型[数组长度];
我们来创建包含 5 个元素的 int 类型的数组,然后分别将元素的值设置为 1、2、3、5 和 8。在 /home/shiyanlou/ 下新建一个文件 Test.java
public class Test {
public static void main(String[] args) {
int[] number = new int[5];
number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;
}
}
实验结果
实验总结
- 数组就是相同数据类型的元素按一定顺序排列的集合。可以把它看成一个大的盒子,里面按顺序存放了多个数据类型相同的数据。
- 尽管数组可以存储一组基本数据类型的元素,但是数组整体属于引用数据类型。当声明一个数组变量时,其实是创建了一个类型为“数据类型[]”(如 int[]、double[]、String[])的数组对象。
实验 31 一维数组操作
实验项目
- 一维数组操作
实验需求
- 操作一维数组
实验内容
要想使用数组,就要先创建数组。所谓创建数组,就是要为数组分配内存空间,不分配内存是不能存放数组元素的,创建数组就是在内存中划分出几个连续的空间用于依次存储数组中的数据元素,其语法形式如下:
数组名 = new 数据类型 [ 数组长度 ];
可以把数组声明和数组创建合并,其语法形式为:
数据类型[] 数组名 = new 数据类型 [ 数组长度 ];
数组长度就是数组最多可存放元素的个数。可以在数组声明的时候初始化数组,或者在声明时就为它分配好空间,这样就不用再为数组分配空间,例如:
int[] engNo = new int[5]; //声明并分配了一个长度为5的int型数组
String[] engName = new String[5]; //声明并分配了一个长度为5的String型数组
分配空间后就可以向数组中放数据了,数组中元素都是通过下标来访问的。 如:
engNo[0]=12;
现在有一个案例:
假设“蓝桥教务系统”中可以存放5个学生的Java成绩,现在需要分别输入这个5个学生的Java成绩,并计算出平均分。
思路:
1)首先我们要分别获取到5个学生的Java成绩,这个时候就需要用到数组了。代码如下:
int[] stu = new int[5]; // 创建一个长度为5的数组
System.out.println("请输入第一个学生的Java成绩:");
stu[0] = input.nextInt();
System.out.println("请输入第二个学生的Java成绩:");
stu[1] = input.nextInt();
System.out.println("请输入第三个学生的Java成绩:");
stu[2] = input.nextInt();
System.out.println("请输入第四个学生的Java成绩:");
stu[3] = input.nextInt();
System.out.println("请输入第五个学生的Java成绩:");
stu[4] = input.nextInt();
现在我们获取到了5个学生的Java成绩,并存入了数组。
2)对成绩进行计算,得到平均分。
int sumScore = 0; //学生成绩总和
double avg = 0; //平均分
sumScore = stu[0]+stu[1]+stu[2]+stu[3]+stu[4];
avg = sumScore/5;
这样我们就可以得到5个学生的平均分了。
其实有细心的同学会发现,上面获取学生的Java成绩的代码出现了重复性的代码,其实我们可以利用for循环,大大减少我们的工作量,更加方便的编写代码,这也是数组的优点之一。完整代码如下:
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int sumScore = 0; // 学生成绩总和
double avg = 0; // 平均分
int[] stu = new int[5]; // 创建一个长度为5的数组
for (int i = 0; i < stu.length; i++) { // stu.length为获取数组长度
System.out.println("请输入第" + (i + 1) + "个学生的Java成绩:");
stu[i] = input.nextInt();
sumScore += stu[i]; // 也可以写成sumScore=stu[i]+sumScore;
}
avg = sumScore / 5;
System.out.println("5个学生的Java成绩平均分是:" + avg);
}
}
你会发现,我们是不是少写了很多代码,减少了代码冗余。
实验结果
实验总结
- 需要注意的是,数组下标从0开始编号,数组名[0]代表数组中第1个元素,数组名[1]代表数组中第2个元素······数组下标的最大值为数组长度减1,如果下标值超过最大值会出现数组下标越界问题。
实验 32 值传递和引用传递
实验项目
- 值传递和引用传递
实验需求
- 验证值传递和引用传递
实验内容
-
我们在代码中实践一下,新建一个Test.java
public class Test { public static void main(String[] args) { int engNo1 = 1001; int engNo2 = 1002; System.out.println("值传递交换数值:"); System.out.println("调用前 工程师1、工程师2 编号为:" + engNo1 + "t" + engNo2); // 调用前 exchange1(engNo1, engNo2); // 值传递,传递的实质是数值的副本,所以没有交换原值 System.out.println("调用后 工程师1、工程师2 编号为:" + engNo1 + "t" + engNo2); // 调用后 int[] engNo = new int[2]; engNo[0] = 1001; engNo[1] = 1002; System.out.println("引用传递交换数值"); System.out.println("调用前 工程师1、工程师2 编号为:" + engNo[0] + "t" + engNo[1]); // 调用前 exchange2(engNo); // 值传递,传递的实质是数值的副本,所以没有交换原值 System.out.println("调用后 工程师1、工程师2 编号为:" + engNo[0] + "t" + engNo[1]); // 调用后 } // 值传递,交换int型a和b的值 public static void exchange1(int a, int b) { int temp = a; a = b; b = temp; } // 引用传递,交换数组x第1个元素和第2个元素的值 public static void exchange2(int[] x) { int temp = x[0]; x[0] = x[1]; x[1] = temp; } }
编译运行:
$ javac Test.java
$ java Test
值传递交换数值:
调用前 工程师1、工程师2 编号为:1001 1002
调用后 工程师1、工程师2 编号为:1001 1002
引用传递交换数值
调用前 工程师1、工程师2 编号为:1001 1002
调用后 工程师1、工程师2 编号为:1002 1001
实验结果
实验总结
- 基本类型和引用类型
Java中的数据类型分为两种为基本类型和引用类型。
- 基本类型的变量保存原始值,所以变量就是数据本身。
常见的基本类型:byte,short,int,long,char,float,double,Boolean
- 引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象。
常见的引用类型:类类型,接口类型和数组。
- 值传递和引用传递
- 值传递
在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下。
- 引用传递
引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间,而引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
基本数据类型传值,对形参的修改不会影响实参;
引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象。
String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。
实验 33 冒泡排序实验
实验 34 选择排序实验
实验 35 快速排序实验
实验 36 二维数组声明实验
实验项目
- 二维数组声明实验
实验需求
- 声明二维数组
实验内容
二维数组也需要声明和分配空间。
语法:
数据类型 [][] 数组名 = new 数据类型[行的个数][列的个数];
//或者
数据类型 [][] 数组名;
数组名 = new 数据类型[行的个数][列的个数];
//也可以
数据类型 [][] 数组名 = {
{第一行值1,第一行值2,...}
{第二行值1,第二行值2,...}
...
}
//二维数组的赋值和访问,跟一维数组类似,可以通过下标来逐个赋值和访问,注意索引从 0 开始
数组名[行的索引][列的索引] = 值;
创建二维数组的时候,可以同时设置第一维长度和第二维长度,也可以只设置第一维长度,但不可以只设置第二维长度。例如:
int[][] arr = new int[3][4];
直观上看,上面的例子就是定义了一个3行4列的二维数组,数组名为arr。该数组的下标变量共有12(3*4),即:
arr[0][0],arr[0][1],arr[0][2],arr[0][3]
arr[1][0],arr[1][1],arr[1][2],arr[1][3]
arr[2][0],arr[2][1],arr[2][2],arr[2][3]
同一维数组一样,二维数组在创建的时候也可以初始化,例如:
int[][] arr1 = { { 2, 3 }, { 1, 5 }, { 3, 9 } }; // 初始化一个3行2列的整型二维数组
int[][] arr2 = { { 1, 2, 3 }, { 1, 5 }, { 3, 9 } }; // 初始化一个3行的整型二维数组
其中,数组arr2第一行有3个元素,第二行和第三行都有2个元素,对于这类每行元素数不同的二维数组,在使用时尤其需要注意数组下标越界的问题。
我们通过代码来实践一下,在/home/project/下新建Test.java
public class Test {
public static void main(String[] args) {
int[][] arr1 = new int[3][4];
int[][] arr2;
arr2 = new int[3][4];
int[][] arr3 = { { 1, 1 }, { 1, 2 }, { 1, 3 } };
}
}
实验结果
实验总结
- 前面介绍的数组只有一个维度,称为一维数组,其数组元素也只有一个下标变量。在实际问题中有很多情况是二维或多维的,Java语言允许构造多维数组存储多维数据。多维数组的数组元素有多个下标,以标识它在数组中的位置。编程中,经常会用到二维数组,更高维度的数组在实际编程中很少使用。
2
实验 37 二维数组赋值实验
实验项目
- 二维数组赋值实验
实验需求
- 接下来完成一个案例:某小组有4个学生,每个学生有3门课的考试成绩,如下表所示。求各科目的平均成绩和总平均成绩。
实验内容
-
我们可以用一维数组来存放科目名称、学生姓名以及各科成绩的和。
String[] course = { "Java基础", "前端技术", "后端技术" }; String[] name = { "王云", "刘静涛", "南天华", "雷静" }; int[] singleSum = new int[] { 0, 0, 0 };
-
用二维数组来存放所有学生的各科成绩。
int[][] stuScore = new int[3][4];
完整代码如下:
在/home/project/下新建Test.java
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String[] course = { "Java基础", "前端技术", "后端技术" };
String[] name = { "王云", "刘静涛", "南天华", "雷静" };
int[][] stuScore = new int[3][4]; // 存放所有学生各科成绩
int[] singleSum = new int[] { 0, 0, 0 }; // 存放各科成绩的和
int allScore = 0; // 存放总成绩
// 输入成绩,对单科成绩累加,对总成绩累加
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
System.out.print("请输入科目:" + course[i] + " 学生:" + name[j] + " 的成绩:");
stuScore[i][j] = input.nextInt(); // 读取学生成绩
singleSum[i] = singleSum[i] + stuScore[i][j]; // 单科成绩累加
}
allScore = allScore + singleSum[i];
}
for (int i = 0; i < 3; i++) {
System.out.println("科目:" + course[i] + "的平均成绩:" + singleSum[i] / 4.0);
}
System.out.println("总平均成绩:" + allScore / 12.0);
}
}
编译运行:
$ javac Test.java
$ java Test
请输入科目:Java基础 学生:王云 的成绩:77
请输入科目:Java基础 学生:刘静涛 的成绩:65
请输入科目:Java基础 学生:南天华 的成绩:91
请输入科目:Java基础 学生:雷静 的成绩:84
请输入科目:前端技术 学生:王云 的成绩:56
请输入科目:前端技术 学生:刘静涛 的成绩:71
请输入科目:前端技术 学生:南天华 的成绩:88
请输入科目:前端技术 学生:雷静 的成绩:79
请输入科目:后端技术 学生:王云 的成绩:80
请输入科目:后端技术 学生:刘静涛 的成绩:81
请输入科目:后端技术 学生:南天华 的成绩:85
请输入科目:后端技术 学生:雷静 的成绩:66
科目:Java基础的平均成绩:79.25
科目:前端技术的平均成绩:73.5
科目:后端技术的平均成绩:78.0
总平均成绩:76.91666666666667
实验结果
实验总结
- 二维数组可以看成是一间有座位的教室,座位一般用第几排的第几个进行定位,每一个座位都有一个行和一个列的属性,一排的座位相当于一个一维数组,所以可以将二维数组简单的理解为是一种“特殊”的一维数组,它的每个数组空间中保存的是一个一维数组。
- 二维数组的赋值和使用与一维数组类似,都是通过下标访问数组元素,不同的是一维数组只有一个下标,而二维数组有两个下标,分别表示该元素所在数组的行数和列数。
实验 38 学生成绩管理系统
实验项目
- 学生成绩管理系统
实验需求
-
当用户输入某个数(非0)时,执行该模块的功能,执行完毕后继续输出主界面。当用户输入0,则退出程序。
-
完成“学生成绩管理系统”第一个模块查询学生信息功能,实际是把学生信息逐行输出。
-
完成“学生成绩管理系统”第二个模块添加学生信息功能,实际是创建学生并给其赋值。
-
完成“学生成绩管理系统”第三个模块修改学生信息功能,实际是找到该学生重新给他赋值。
-
完成“学生成绩管理系统”第四个模块删除学生信息功能,实际是用后一个学生的信息覆盖所要删除的学生信息。
实验内容
思路:
-
首先做出菜单栏,我们可以选择用switch分支来做。
-
封装一个添加学生信息的方法,利用数组存放学生信息,用for循环循环输入。
-
封装一个查询学生信息的方法,利用for循环逐行输出学生信息。
-
封装一个修改学生信息的方法,利用学生的姓名或者学号用for循环找到对应的学生再给他重新赋值。
-
封装一个删除学生信息的方法,利用for循环找到对应的学生,然后再用for循环逐行覆盖。
完整代码如下:
在/home/project/下新建ScoreManager.java
import java.util.Scanner;
public class ScoreManager {
// 扫描器
static Scanner input = new Scanner(System.in);
// 30个学生姓名
static String[] name = new String[30];
// 年龄
static int[] age = new int[30];
// 学号
static int[] id = new int[30];
// 学生个数
static int count = 0;
static boolean YN = false;
static String chooseYN = "Y";
public static void main(String[] args) {
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.print("请输入您的选择:(1、2、3、4、5)");
int choose = input.nextInt();
switch (choose) {
case 1:
select();
break;
case 2:
add();
break;
case 3:
update();
break;
case 4:
delete();
break;
default:
break;
}
System.out.println("是否继续进入主菜单");
if (!isContinue()) {
break;
}
}
}
// 查询信息方法
public static void select() {
while (true) {
System.out.println("序号" + "t" + "姓名" + "t" + "年龄" + "t" + "学号");
for (int i = 0; i < count; i++) {
System.out.println((i + 1) + "t" + name[i] + "t" + age[i] + "t" + id[i]);
}
System.out.println("是否继续查询学生信息?(Y/N)");
if (!isContinue()) {
break;
}
}
}
// 查询所有信息方法
public static void selectAll() {
System.out.println("序号" + "t" + "姓名" + "t" + "年龄" + "t" + "学号");
for (int i = 0; i < count; i++) {
System.out.println((i + 1) + "t" + name[i] + "t" + age[i] + "t" + id[i]);
}
}
// 添加信息方法
public static void add() {
while (true) {
System.out.print("请输入您的姓名:");
name[count] = input.next();
System.out.print("请输入您的年龄:");
age[count] = input.nextInt();
System.out.print("请输入您的学号:");
id[count] = input.nextInt();
count++;
selectAll();
System.out.println("是否继续添加?(Y/N)");
if (!isContinue()) {
break;
}
}
}
// 修改信息方法
public static void update() {
selectAll();
System.out.println("请输入您要修改的学生ID");
int oldID = input.nextInt();
System.out.println("请输入您的新姓名");
String newName = input.next();
for (int i = 0; i < count; i++) {
if (id[i] == oldID) {
name[i] = newName;
}
}
selectAll();
}
// 删除信息方法
public static void delete() {
while (true) {
System.out.println("请输入您要删除的学生的学号:");
int oldID = input.nextInt();
for (int i = 0; i < count; i++) {
if (id[i] == oldID) {
for (int j = i; j < count - i; j++) {
name[i] = name[j + 1];
age[i] = age[j + 1];
id[i] = id[j + 1];
}
}
}
System.out.println("删除成功!");
count--;
System.out.println("是否继续删除学生信息?");
if (!isContinue()) {
break;
}
}
}
// 是否返回方法
public static boolean isContinue() {
chooseYN = input.next();
if (chooseYN.equalsIgnoreCase("n")) {
return false;
}
return true;
}
}
实验结果
- 功能2添加学生信息和功能1查询学生信息
- 功能3修改学生信息
- 功能4删除学生信息
- 功能5退出
实验总结
增删改查
(五)String和JavaAPI
实验 39 String创建实验
实验项目
- String创建实验
实验需求
- String创建
实验内容
字符串是 Java 中特殊的类,使用方法像一般的基本数据类型,被广泛应用在 Java 编程中。Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个 String 类来创建和操作字符串。
在 Java 中定义一个字符串最简单的方法是用双引号把它包围起来。这种用双引号括起来的一串字符实际上都是 String 对象,如字符串“Hello”在编译后即成为 String 对象。因此也可以通过创建 String 类的实例来定义字符串。
不论使用哪种形式创建字符串,字符串对象一旦被创建,其值是不能改变的,但可以使用其他变量重新赋值的方式进行更改。
- 注意:由于类和对象的的内容在后面才会讲解,所以只要遇到类和对象的概念,知道如何操作就可以了,不需要明白为什么要这样使用。
直接定义字符串
直接定义字符串是指使用双引号表示字符串中的内容,例如“Hello Java”、“Java 编程”等。具体方法是用字符串常量直接初始化一个 String 对象,示例如下:
String str = "Hello Java";
或者:
String str;
str = "Hello Java";
注意:字符串变量必须经过初始化才能使用。
使用 String 类定义
前面我们提到在 Java 中每个双引号定义的字符串都是一个 String 类的对象。因此,可以通过使用 String 类的构造方法来创建字符串,该类位于 java.lang 包中(关于 Java 常用的包,后面的实验会详细讲解)。
- String 类的构造方法有多种重载形式,每种形式都可以定义字符串。下面介绍最常用的几种形式。
1)String()
初始化一个新创建的 String 对象,表示一个空字符序列。
2)String(String original)
初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列。换句话说,新创建的字符串是该参数字符串的副本。例如:
String str1 = new String("Hello Java");
String str2 = new String(str1);
这里 str1 和 str2 的值是相等的。
3)String(char[ ]value)
分配一个新的字符串,将参数中的字符数组元素全部变为字符串。该字符数组的内容已被复制,后续对字符数组的修改不会影响新创建的字符串。例如:
char a[] = {'H','e','l','l','0'};
String sChar = new String(a);
a[1] = 's';
上述 sChar 变量的值是字符串“Hello”。 即使在创建字符串之后,对 a 数组中的第 2 个元素进行了修改,但未影响 sChar 的值。
实验总结
- String字符串是常量,字符串的值在创建后不能更改。
- String类是最终类,不能被继承。
实验 40 String常用方法实验
实验项目
- String常用方法实验
实验需求
- String常用方法
实验内容
-
新建一个源代码文件TestArrayMethod.java
public class TestArrayMethod { public static void main(String[] args) { String s1 = "blue bridge"; String s2 = "Blue Bridge"; System.out.println(s1.charAt(1)); // 查找第2个字符,结果位1 System.out.println(s1.length()); // 求s1的长度,结果为1 System.out.println(s1.indexOf("bridge")); // 查找bridge字符串在s1中的位置,结果为5 System.out.println(s1.indexOf("Bridge")); // 查找Bridge字符串在s1中的位置,没找到,返回-1 System.out.println(s1.equals(s2)); // 区分大小写比较,返回false System.out.println(s1.equalsIgnoreCase(s2)); // 不区分大小写比较,返回true String s = "我是学生,我在学java!"; String str = s.replace('我', '你'); // 把“我”替换成“你” System.out.println(str); } }
实验结果
实验总结
- 在Java语言中,所有类似“ABC”的字面值,都是String类的实例;String类位于java.lang包下,是Java语言的核心类,提供了字符串的比较、查找、截取、大小写转换等操作;
- 以下是String类的常用方法
-
public char charAt(int index)
从字符串中返回指定索引处的字符值
-
public int length()
返回此字符串的长度。这里需要和获取数组长度区别开,获取数组长度是通过“数组名.length”获取的
-
public int indexOf(String str)
返回指定子字符串在此字符串中第一次出现处的索引
-
public int indexOf(String str,int fromIndex)
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始搜索
-
public boolean equalsIgnoreCase(String another)
将此String与另一个String比较,不区分大小写
-
public String replace(char oldChar,char new Char)
返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的
实验 41 StringBuilder类实验
实验项目
- StringBuilder类实验
实验需求
- 练习StringBuilder类
实验内容
-
新建 StringBuilderTest.java。
public class StringBuilderTest { public static void main(String[] args){ //定义和初始化一个StringBuilder类的字串s StringBuilder s = new StringBuilder("I"); //在s后面添加字串" java" s.append(" java"); //在s[1]的位置插入字串 s.insert(1, " love"); String t = s.toString(); //转为字符串 System.out.println(t); } }
输出结果为: I love java。
实验结果
实验总结
- StringBuilder 类是可变的。它是 String 的对等类,它可以增加和编写字符的可变序列,并且能够将字符插入到字符串中间或附加到字符串末尾(当然是不用创建其他对象的)
- StringBuilder 的构造方法:
实验 42 StringBuffer类实验
实验项目
- StringBuffer类实验
实验需求
- 练习StringBuffer类
实验内容
以下是StringBuffer类最常用的构造方法。
- StringBuffer():构造一个其中不带字符的字符串缓冲区,其初识容量为16个字符。
- StringBuffer(String str):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
以下是通过StringBuffer类的构造方法创建StringBuffer字符串的代码
StringBuffer strB1 = new StringBuffer();
通过strB1.length()返回长度是0,但在底层创建了一个长度为16的字符数组。
StringBuffer strB2 = new StringBuffer("柳海龙");
通过strB2.length()返回长度是3,在底层创建了一个长度为3+16的字符数组。
StringBuffer上的主要操作是append和insert方法,将字符追加或插入到字符缓冲区中。append方法始终将字符添加到缓冲区的末端,而insert方法则在指定的位置添加字符。
接下来我们通过代码来说明StringBuffer类方法的使用,新建 TestStringBuffer.java。
public class TestStringBuffer {
public static void main(String[] args) {
System.out.println("创建StringBuffer对象");
// 使用StringBuffer()构造器创建对象
StringBuffer strB1 = new StringBuffer();
System.out.println("new StringBuffer()创建对象的长度为:" + strB1.length());
// 使用StringBuffer(String str)构造器创建对象
StringBuffer strB2 = new StringBuffer("柳海龙");
System.out.println("new StringBuffer("柳海龙")创建对象的长度为:" + strB2.length());
System.out.println("strB2里的内容为:" + strB2);
// 使用append、insert方法追加、插入字符串
System.out.println("使用append方法追加字符串");
strB2.append(",您好!"); // 在最后增加“,您好!”
System.out.println(strB2);
strB2.insert(3, "工程师"); // 从第4个位置开始,插入“工程师”
System.out.println(strB2);
}
}
实验结果
实验总结
- 在 Java 中,除了通过 String 类创建和处理字符串之外,还可以使用 StringBuffer 类来处理字符串。StringBuffer 类可以比 String 类更高效地处理字符串。
- StringBuffer又称为可变字符序列,它是一个类似于 String 的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。原来StringBuffer是个字符串的缓冲区,即就是它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。
实验 43 Math类常用方法实验
实验项目
- Math类常用方法实验
实验需求
- 练习Math类常用方法
实验内容
- 求最大值、最小值和绝对值
在程序中常见的就是求最大值、最小值和绝对值问题,如果使用 Math 类提供的方法可以很容易实现。
需求:求 10 和 20 的较大值、15.6 和 15 的较小值、-12 的绝对值,新建一个 Test01.java。代码如下:
public class Test01 {
public static void main(String[] args) {
System.out.println("10 和 20 的较大值:" + Math.max(10, 20));
System.out.println("15.6 和 15 的较小值:" + Math.min(15.6, 15));
System.out.println("-12 的绝对值:" + Math.abs(-12));
}
}
该程序的运行结果如下:
$ javac Test01.java
$ java Test01
10和20的较大值:20
15.6和15的较小值:15.0
-12的绝对值:12
- 求整运算
Math 类的求整方法有很多,下面的实例演示了 Math 类中取整函数方法的应用,新建一个 Test02.java。代码如下:
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.outprintln("请输入一个数字:");
double num = input.nextDouble();
System.out.println("大于或等于 "+ num +" 的最小整数:" + Math.ceil(num));
System.out.println("小于或等于 "+ num +" 的最大整数:" + Math.floor(num));
System.out.println("将 "+ num +" 加上 0.5 之后最接近的整数:" + Math.round(num));
System.out.println("最接近 "+num+" 的整数:" + Math.rint(num));
}
}
编译运行:
$ javac Test02.java
$ java Test02
请输入一个数字:
99.01
大于或等于 99.01 的最小整数:100.0
小于或等于 99.01 的最大整数:99.0
将 99.01 加上 0.5 之后最接近的整数:100
最接近 99.01 的整数:99.0
实验结果
实验总结
- Java 中的 +、-、*、/ 和 % 等基本算术运算符不能进行更复杂的数学运算,例如,三角函数、对数运算、指数运算等。于是 Java 提供了 Math 工具类来完成这些复杂的运算。
- 在 Java 中 Math 类封装了常用的数学运算,提供了基本的数学操作,如指数、对数、平方根和三角函数等。Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 Math 类的对象,并且 Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。
下面是Math类的常见方法:
- 类包含用于执行基本数学运算的方法
- Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
- Math所有类都是静态的。可以直接类名。调用。
实验 44 Date类实验
实验项目
- Date类实验
实验需求
- 练习Date类
实验内容
-
Date date1 = new Date(); // 调用无参数构造函数 System.out.println(date1.toString()); // 输出:Sun Jun 14 19:25:21 GMT+08:00 2020 Date date2 = new Date(60000); // 调用含有一个long类型参数的构造函数 System.out.println(date2); // 输出:Thu Jan 01 08:01:00 GMT+08:00 1970
Date 类的无参数构造方法获取的是系统当前的时间,显示的顺序为星期、月、日、小时、分、秒、年。
- 下面使用一个实例来具体演示 Date 类的使用。假设,某一天特定时间要去做一件事,而且那个时间已经过去一分钟之后才想起来这件事还没有办,这时系统将会提示已经过去了多 长时间。具体的代码如下:
import java.util.Date; import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入要做的事情:");
String title = input.next();
Date date1 = new Date(); // 获取当前日期
System.out.println("[" + title + "] 这件事发生时间为:" + date1);
try {
Thread.sleep(60000);// 暂停 1 分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
Date date2 = new Date();
System.out.println("现在时间为:" + date2);
if (date2.before(date1)) {
System.out.println("你还有 " + (date2.getTime() – date1.getTime()) / 1000 + " 秒需要去完成【" + title + "】这件事!");
} else {
System.out.println("【" + title + "】事情已经过去了 " + (date2.getTime() – date1.getTime()) / 1000 + " 秒");
}
}
}
在该程序中,分别使用 Date 类的无参数构造方法创建了两个 Date 对象。在创建完第一个 Date 对象后,使用 Thread.sleep() 方法让程序休眠 60 秒,然后再创建第二个 Date 对象,这样第二个 Date 对象所表示的时间将会在第一个 Date 对象所表示时间之后,因此“date2.before(date1)”条件表达式不成立,从而执行 else 块中的代码,表示事情已经发生过。
运行该程序,执行结果如下所示:
$ javac Test.java
$ java Test
请输入要做的事情:
收快递
[收快递] 这件事发生时间为:Sun Jun 14 19:30:40 GMT+08:00 2020
现在时间为:Sun Jun 14 19:31:40 GMT+08:00 2020
【收快递】事情已经过去了 60 秒
### 实验结果
1.
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210206-1612600173690" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. Date 类表示日期和时间,里面封装了操作日期和时间的方法。Date 类经常用来获取系统当前时间。
2. 构造方法
来看看类 Date 中定义的未过时的构造方法:

3. 常用方法
Date 类提供了许多与日期和事件相关的方法,其中常见的方法如下表所示。

4. Date类是从JDK1.1就开始存在的老类,其提供了针对日期进行操作的诸多方法,但其却一直饱受诟病,不同的起始编号,国际化的低支持,JDK官方也认识到这个问题,后台提出使用Calendar类进行日期操作,日期的格式化交给DateFormat,虽然我们已经不再使用Date类中的大多数方法,但是还有一部分保留的内容值得我们一谈。
1. Date 类表示日期和时间,里面封装了操作日期和时间的方法。Date 类经常用来获取系统当前时间。
2.

3. Date类是从JDK1.1就开始存在的老类,其提供了针对日期进行操作的诸多方法,但其却一直饱受诟病,不同的起始编号,国际化的低支持,JDK官方也认识到这个问题,后台提出使用Calendar类进行日期操作,日期的格式化交给DateFormat,虽然我们已经不再使用Date类中的大多数方法,但是还有一部分保留的内容值得我们一谈。
## 实验 45 Calendar类实验
### 实验项目
* Calendar类实验
### 实验需求
1. 练习Calendar类
### 实验内容
1. TimeZone 是 java.util 包中的一个类,其中封装了有关时区的信息。每一个时区对应一组 ID。类 TimeZone 提供了一些方法完成时区与对应 ID 两者之间的转换。
例如:
//太平洋时区的 ID 为 PST
TimeZone tz0 = TimeZone.getTimeZone("PST")
//getDefault()可以获取主机所处时区的对象
TimeZone tz1 = TimeZone.getDefault()
Locale 只是一种机制,它用来标识一个特定的地理、政治或文化区域获取一个 Locale 对象的构造方法:
//调用Locale类的构造方法
Locale l0 = new Locale(String language)
Locale l1 = new Locale(String language, String country)
Locale l2 = new Locale(String languge, String country, String variant)
//调用Locale类中定义的常量
Locale l1 = Locale.CHINA
### 实验总结
1. Calendar类的功能要比Date类强大很多,可以方便的进行日期的计算,获取日期中的信息时考虑了时区等问题。而且在实现方式上也比Date类要复杂一些
2. 在早期的 JDK 版本中,Date 类附有两大功能:
* 允许用年、月、日、时、分、秒来解释日期。
* 允许对表示日期的字符串进行格式化和句法分析。
在 JDK1.1 中提供了类 Calendar 来完成第一种功能,类 DateFormat 来完成第二项功能。DateFormat 是 java.text 包中的一个类。与 Date 类有所不同的是,DateFormat 类可以接受用各种语言和不同习惯表示的日期字符串。
但是 Calendar 类是一个抽象类,它完成 Date 类与普通日期表示法之间的转换,而我们更多的是使用 Calendar 类的子类 GregorianCalendar 类。它实现了世界上普遍使用的公历系统。当然我们也可以继承 Calendar 类,然后自己定义实现日历方法。
先来看一看 GregorianCalendar 类的构造函数:

1. Calendar类的功能要比Date类强大很多,可以方便的进行日期的计算,获取日期中的信息时考虑了时区等问题。而且在实现方式上也比Date类要复杂一些
2.
## 实验 46 DateFormat类实验
### 实验项目
* DateFormat类实验
### 实验需求
1. 练习DateFormat类
### 实验内容
1. 使用 DateFormat 类格式化曰期/时间的示例如下,新建一个源代码文件 DateFormatDemo.java。
// 获取不同格式化风格和中国环境的日期
DateFormat df1 = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CHINA);
DateFormat df2 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA);
DateFormat df3 = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.CHINA);
DateFormat df4 = DateFormat.getDateInstance(DateFormat.LONG, Locale.CHINA);
// 获取不同格式化风格和中国环境的时间
DateFormat df5 = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.CHINA);
DateFormat df6 = DateFormat.getTimeInstance(DateFormat.FULL, Locale.CHINA);
DateFormat df7 = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.CHINA);
DateFormat df8 = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA);
// 将不同格式化风格的日期格式化为日期字符串
String date1 = df1.format(new Date());
String date2 = df2.format(new Date());
String date3 = df3.format(new Date());
String date4 = df4.format(new Date());
// 将不同格式化风格的时间格式化为时间字符串
String time1 = df5.format(new Date());
String time2 = df6.format(new Date());
String time3 = df7.format(new Date());
String time4 = df8.format(new Date());
// 输出日期
System.out.println("SHORT:" + date1 + " " + time1);
System.out.println("FULL:" + date2 + " " + time2);
System.out.println("MEDIUM:" + date3 + " " + time3);
System.out.println("LONG:" + date4 + " " + time4);
运行该段代码,输出的结果如下:
$ javac Test.java
$ java Test
SHORT:20-6-14 下午7:39
FULL:2020年6月14日 星期日 下午07时39分16秒 GMT+08:00
MEDIUM:2020-6-14 19:39:16
LONG:2020年6月14日 下午07时39分16秒
### 实验结果
1. <img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210206-1612600669658" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. DateFormat是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化和分析日期或时间。DateFormat类中最重要的两个方法是format和parse,简单来说format(是将日期Date类的对象转化为用户所能看懂的字符串形式如2019年1月28日 )parse(是将用户所能看懂的字符串形式的日期转化为日期Date类)
2. DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期→文本)、解析(文本→日期)和标准化日期。
在创建 DateFormat 对象时不能使用 new 关键字,而应该使用 DateFormat 类中的静态方法 getDateInstance(),示例代码如下:
DateFormat df = DateFormat.getDatelnstance();
在创建了一个 DateFormat 对象后,可以调用该对象中的方法来对日期/时间进行格式化。DateFormat 类中常用方法如下表所示。

格式化样式主要通过 DateFormat 常量设置。将不同的常量传入到表 1 所示的方法中,以控制结果的长度。DateFormat 类的常量如下。
* SHORT:完全为数字,如 12.5.10 或 5:30pm。
* MEDIUM:较长,如 May 10,2016。
* LONG:更长,如 May 12,2016 或 11:15:32am。
* FULL:是完全指定,如 Tuesday、May 10、2012 AD 或 11:l5:42am CST。
1. DateFormat是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化和分析日期或时间。DateFormat类中最重要的两个方法是format和parse,简单来说format(是将日期Date类的对象转化为用户所能看懂的字符串形式如2019年1月28日 )parse(是将用户所能看懂的字符串形式的日期转化为日期Date类)
2. 要学会熟练使用jdk自带的各种包中的各种类和各种方法;
3.

4. 格式化样式主要通过 DateFormat 常量设置。将不同的常量传入到DateFormat常用的方法中,以控制结果的长度。DateFormat 类的常量如下。
* SHORT:完全为数字,如 12.5.10 或 5:30pm。
* MEDIUM:较长,如 May 10,2016。
* LONG:更长,如 May 12,2016 或 11:15:32am。
* FULL:是完全指定,如 Tuesday、May 10、2012 AD 或 11:l5:42am CST。
## 实验 47 中国万年历
### 实验项目
* 中国万年历
### 实验需求
1. 练习中国万年历实验
### 实验内容
编写思路:
1. 先输出提示语句,并接受用户输入的年、月
2. 根据用户输入的年,先判断是否是闰年。
3. 根据用户输入的月来判断月的天数。
4. 用循环计算用户输入的年份距1900年1月1日的总天数。
5. 用循环计算用户输入的月份距输入的年份的1月1日共有多少天。
6. 相加4与5的天数,得到总天数。
7. 用总天数来计算输入月的第一天的星期数。
8. 根据7的值,格式化输出这个月的日历。
新建一个源代码文件 DaysCelandar.java。
import java.util.Scanner;
public class DaysCelandar {
public static void main(String[] args) {
// 思路:
// 1、构建Scanner扫描器类的对象
Scanner input = new Scanner(System.in);
// 2、从控制台接收用户录入的年份、月份
System.out.println("请输入年份");
int year = input.nextInt();
System.out.println("请输入月份");
int month = input.nextInt();
// 3、获得1900年距离当前年份上一年的总天数
int totalDays = getFromNowYearTo1900TotalDays(year);
// 4、获得当前年份所经过的天数
int totalDaysThisYear = getNowYearPassedTotalDays(year, month);
// 5、求得给定月份第一天的星期数
int week = (totalDays + totalDaysThisYear + 1) % 7;// 星期日是,week = 0
// 6、格式化输出日历
formatCalendarOutput(week, year, month);
}
/**
* 格式化输出日历
*
* @param week:当前月份第一天的星期数
* @param year:当前年份
* @param month:当前月份
*/
public static void formatCalendarOutput(int week, int year, int month) {
int cnt = 0;// 计数器,记录空白数和日期数的和
// 1) 打印表头
System.out.println("星期日t星期一t星期二t星期三t星期四t星期五t星期六");
// 2)打印空白(观察星期与之前空个数之间的规律)
for (int i = 1; i <= week; i++) {
System.out.print("t");
cnt++;
}
// 3) 打印日历
for (int i = 1; i <= getNowMonthDays(year, month); i++) {
System.out.print(i + "t");
cnt++;
// 若记录空白数和日期数的和是七的倍数,应该换行输出
if (cnt % 7 == 0) {
System.out.println();
}
}
}
/**
* 判断给定的年份是否为闰年
*
* @param year:给定的年份
* @return true:闰年;false:平年
*/
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
/**
* 根据参数指定的年份,月份,求出当前月的总天数
*
* @param year:年份
* @param month:月份
* @return 月的总天数
*/
public static int getNowMonthDays(int year, int month) {// year:设计该参数的原因,2月份根据年份是否是闰年来确定其天数的
switch (month) {
case 2:
return isLeapYear(year) ? 29 : 28;
case 4:
case 6:
case 9:
case 11:
return 30;
default:
return 31;
}
}
/**
* 获得当前年份的上一年距离1900年所经过的总天数
*
* @param year 当前年份
* @return 总天数
*/
public static int getFromNowYearTo1900TotalDays(int year) {
int totalDays = 0;
for (int i = 1900; i < year; i++) {// i:年份
totalDays += isLeapYear(i) ? 366 : 365;
}
return totalDays;
}
/**
* 求出当前年份经过的总天数(从当前年的1月1日到给定月份的上一个月最末一天)
*
* @param year:年份
* @param month:月份
* @return 总天数
*/
public static int getNowYearPassedTotalDays(int year, int month) {
int totalDays = 0;
for (int i = 1; i < month; i++) {// i:月份
totalDays += getNowMonthDays(year, i);
}
return totalDays;
}
}
### 实验结果
1. <img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210206-1612600873705" alt="图片.png" style="zoom:200%;" />
### 实验总结
1.
1.先输出提示语句,并接受用户输入的年、月
2.根据用户输入的年,先判断是否是闰年。
3.根据用户输入的月来判断月的天数。
4.用循环计算用户输入的年份距1900年1月1日的总天数。
5.用循环计算用户输入的月份距输入的年份的1月1日共有多少天。
6.相加4与5的天数,得到总天数。
7.用总天数来计算输入月的第一天的星期数。
8.根据7的值,格式化输出这个月的日历。
1.

2. 编写程序首先要理顺思路,这样程序就成功了一半。
## 实验 48 System类和Runtime类实验
### 实验项目
* System类和Runtime类实验
### 实验需求
1. 练习System类和Runtime类实验
### 实验内容
System类
System 类提供了以下功能:
* 标准输入,标准输出和错误输出流;
* 访问外部定义的属性和环境变量;
* 加载文件和库的方法;
* 以及用于快速复制数组的实用方法。
System 不可以被实例化,只可以使用其静态方法。
//从指定的源数组中复制一个数组,从源数组指定的位置开始,到目标数组指定的位置
public static void arraycopy(Object src,int srcPos, Object dest,int desPos,int length)
//返回以毫秒为单位的当前时间(从1970年到现在的毫秒数)
public static long currentTimeMillis()
//终止当前正在运行的Java虚拟机,status为 0时退出
public static void exit(int status)
// 运行垃圾收集器
public static void gc()
// 取得当前系统的全部属性
public static Properties getProperties()
//获取指定键的系统属性
public static String getProperty(String key)
新建一个源代码文件 SystemDemo.java。
import java.util.Arrays;
public class SystemDemo {
public static void main(String[] args) {
int[] a = {7, 8, 9, 10, 11};
int[] b = {1, 2, 3, 4, 5, 6};
//从数组a的第二个元素开始,复制到b数组的第三个位置 复制的元素长度为3
System.arraycopy(a, 1, b, 2, 3);
//输出结果
System.out.println(Arrays.toString(b));
System.out.println("当前时间:" + System.currentTimeMillis());
System.out.println("java版本信息:" + System.getProperty("java.version"));
//运行垃圾收集器
System.gc();
//退出
System.exit(0);
}
}
编译运行:
$ javac SystemDemo.java
$ java SystemDemo
[1, 2, 8, 9, 10, 6]
当前时间:1544670501472
java版本信息:11
Runtime类
Runtime类代表Java程序的运行时环境,可以访问JVM的相关信息,每个Java程序都有一个与之对应的Runtime实例,应用程序通过该对象与其运行时环境相连。应用程序不能创建自己的Runtime实例,但可以通过getRuntime()方法获取与之关联的Runtime对象。
public class TestRunTime {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
System.out.println("本机CPU内核数:" + runtime.availableProcessors());
System.out.println("最大可用内存空间" + runtime.maxMemory() / 1024 / 1024 + "MB,默认为系统的1/4");
System.out.println("可用内存空间:" + runtime.totalMemory() / 1024 / 1024 + "MB,默认为系统的1/64");
System.out.println("空闲内存空间:" + runtime.freeMemory() / 1024 / 1024 + "MB");
System.out.println("手工GC处理gc()");
runtime.gc();
System.out.println("什么是GC?可以由系统自动调用的垃圾释放功能,或者RunTime手工调用的垃圾释放功能");
}
}
编译运行:
$ javac TestRunTime.java
$ java TestRunTime
本机CPU内核数:4
最大可用内存空间3566MB,默认为系统的1/4
可用内存空间:241MB,默认为系统的1/64
空闲内存空间:238MB
手工GC处理gc()
什么是GC?可以由系统自动调用的垃圾释放功能,或者RunTime手工调用的垃圾释放功能
### 实验结果
1. <img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210206-1612601059926" alt="图片.png" style="zoom:200%;" />
2.
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210206-1612601087538" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. Java程序在不同操作系统上运行时,可能需要取得平台相关的属性,或者调用平台命令来完成特定功能。java提供了System类与Runtime类来与程序的运行平台进行交互。
1. Java程序在不同操作系统上运行时,可能需要取得平台相关的属性,或者调用平台命令来完成特定功能。java提供了System类与Runtime类来与程序的运行平台进行交互。
2. System类
System 类提供了以下功能:
* 标准输入,标准输出和错误输出流;
* 访问外部定义的属性和环境变量;
* 加载文件和库的方法;
* 以及用于快速复制数组的实用方法。
System 不可以被实例化,只可以使用其静态方法。
3.

4. 运行垃圾收集器:System.gc();
5. 退出:System.exit(0);
6.

7. 在System类里面定义的几个重要的方法:
* 取得当前的系统时间:public static long currentTimeMillis()。
* System类中调用Runtime的gc()方法:public static void gc()。
Runtime类是直接与本地运行有关的所有相关属性的集合,所以在Runtime类里面定义有如下的几个方法:
* 返回所有可用内存空间:public long totalMemory()。
* 返回所有最大内存空间:public long maxMemory()。
* 返回空余内存空间:public long freeMemory()。
# (六)类和对象
## 实验 49 声明Java类实验
### 实验项目
* 声明Java类实验
### 实验需求
1. 声明Java类
### 实验内容
1. 定义类名,用于区分不同的类。如下代码中 `public class `后面跟的就是类名。`class`是声明类的关键字,类名后面跟上大括号,大括号里面就是类的一些信息。`public `为权限修饰符。
public class 类名 {
//定义属性部分(成员变量)
属性1的类型 属性1;
属性2的类型 属性2;
…
//定义方法部分
方法1
方法2
…
}
2. 编写类的属性。对象有什么,需要通过属性来表示。属性的定义是写在类名后面的大括号里,在定义属性时,要明确属性的类型。在一个类当中可以写一个或多个属性。当然也可以不定义属性。
3、编写类的方法。方法也是写在大括号里面。可以定义一个方法或多个方法,当然也可以不定义方法。
在` home/project/ `目录下新建文件` People.java`:
public class People {
//属性(成员变量) 有什么
double height; //身高
int age; //年龄
int sex; //性别,0为男性,非0为女性
//方法 干什么
void cry(){
System.out.println("我在哭!");
}
void laugh(){
System.out.println("我在笑!");
}
void printBaseMes(){
System.out.println("我的身高是"+height+"cm");
System.out.println("我的年龄是"+age+"岁");
if(this.sex==0)
System.out.println("我是男性!");
else
System.out.println("我是女性!");
}
}
### 实验总结
1. 类是相同或相似对象的一种抽象,是对象的一个模板,它描述一类对象的行为和状态。
2. 类是具有相同属性和方法(行为)的对象的集合。
3. 类就好像是钞票模板,对象就好像是钞票;
4. 使用类的属性或者方法需要创建对象,除非有static修饰

## 实验 50 创建对象实验
### 实验项目
* 创建对象实验
### 实验需求
1. 创建对象
### 实验内容
1. 创建对象后,就要使用对象了,使用对象无非就是对属性和方法进行操作和调用。语法如下
//引用对象属性
对象名.属性
//引用对象方法
对象名.方法
例如对 LiLei 的身高(length)赋值,并调用 cry 这个方法
例如对 LiLei 的身高(length)赋值,并调用 cry 这个方法
* 成员变量就是指的对象的属性,是在类中定义,来描述对象的特性。还有一种变量叫局部变量,它是由类的方法定义,在方法中临时保存数据。
* 在使用时注意,成员变量可以被本类的所有方法所使用,同时可以被与本类有关的其他类所使用。而局部变量只能在当前的方法中使用。
2. 创建对象的语法如下:
类名 对象名 = new 类名();
比如对 People这个类,我想实例化LiLei这个人。LiLei 的数据类型便是 People 这个类型。(类可以看成使我们自己定义的数据类型)
People LiLei = new People();
定义类的时候不会为类开辟内存空间,但是一旦创建了对象,系统就会在内存中为对象开辟一块空间,用来存放对象的属性值和方法。新建一个 NewObject.java 文件。
public class NewObject {
public static void main(String[] args) {
People LiLei = new People(); //创建一个People对象LiLei
LiLei.height =170;
LiLei.age = 20;
LiLei.sex = 1;
LiLei.printBaseMes();
}
}
### 实验结果
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612495421004" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. 作用域可以简单地理解为变量的生存期或者作用范围,也就是变量从定义开始到什么时候消亡。
2. 局部变量的作用域仅限于定义它的方法内。而成员变量的作用域在整个类内部都是可见的。
3. 同时在相同的方法中,不能有同名的局部变量;在不同的方法中,可以有同名的局部变量。
4. 成员变量和局部变量同名时,局部变量具有更高的优先级。 大家可以编写代码验证一下。
5. Java 是面向对象的语言,而他的体现就在于 Java 程序都以类 class 为组织单元。而一个类是对象的抽象,所以类由属性和方法两部分组成。
## 实验 51 创建封装实验
### 实验项目
* 创建封装实验
### 实验需求
1. 实现类的封装
### 实验内容
1. 首先在类里要将属性前添加 private 修饰符。然后定义 getter和 setter 方法。修改 People.java 和 NewObject.java 的内容如下。
public class People {
//属性(成员变量)有什么,前面添加了访问修饰符private
//变成了私有属性,必须通过方法调用
private double height; //身高
//属性已经封装好了,如果用户需要调用属性
//必须用getter和setter方法进行调用
//getter和setter方法需要程序员自己定义
public double getHeight(){
//getter 方法命名是get关键字加属性名(属性名首字母大写)
//getter 方法一般是为了得到属性值
return height;
}
//同理设置我们的setter方法
//setter 方法命名是set关键字加属性名(首字母大写)
//setter 方法一般是给属性值赋值,所以有一个参数
public void setHeight(double newHeight){
height = newHeight;
}
}
2. 现在 main 函数里的对象,不能再直接调用属性了,只能通过 getter 和 setter 方法进行调用。
public class NewObject {
public static void main(String[] args) {
People LiLei = new People(); //创建了一个People对象LiLei
//利用setter方法为属性赋值
LiLei.setHeight(170.0);
//利用getter方法取属性值
System.out.println("LiLei的身高是"+LiLei.getHeight());
}
}
### 实验结果
1.
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612495777763" alt="图片.png" style="zoom:200%;" />
2.
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612495806023" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
2. 如何去实现类的封装呢?
* 修改属性的可见性,在属性的前面添加修饰符 (private)
* 对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值)方法,用于对私有属性的访问
* 在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定。

如果没有在属性前面添加任何修饰符,通过对象就可以直接对属性值进行修改,没有体现封装的特性。这在许多程序设计中都是不安全的,所以需要利用封装,来改进代码。
## 实验 52 声明无参构造方法
### 实验项目
* 声明无参构造方法
### 实验需求
1. 声明无参构造方法
### 实验内容
1. 构造方法的名称与类名相同,且没有返回值。它的语法格式如下:
//与类同名,可以指定参数,没有返回值
public 构造方法名(){
//初始化代码
}
### 实验总结
1. 在面向对象中有一个非常重要的知识点,就是构造方法。每个类都有构造方法,在创建该类的对象的时候他们将被调用,如果没有定义构造方法,Java 编译器会提供一个默认构造方法。 创建一个对象的时候,至少调用一个构造方法。
2. 如果在定义类的时候没有写构造方法,系统会默认生成一个无参构造方法,这个构造方法什么也不会做。
3. 当有指定的构造方法时,系统都不会再添加无参构造方法了。
## 实验 53 声明带参构造方法
### 实验项目
* 声明带参构造方法
### 实验需求
1. 声明带参构造方法
### 实验内容
1. 下面是一个构造方法的例子:
public class People{
//无参构造方法
public People(){
}
//有一个参数的构造方法
public People(int age){
}
}
又例如具体的构造方法:
public class People {
//属性(成员变量)有什么
double height; //身高
int age; //年龄
int sex; //性别,0为男性,非0为女性
//构造函数,初始化了所有属性
public People(double h, int a, int s){
height = h;
age = a;
sex = s;
}
}
//创建对象,调用我们自己定义的有参构造方法
People XiaoMing = new People(168, 21, 1);
### 实验总结
1. 在面向对象中有一个非常重要的知识点,就是构造方法。每个类都有构造方法,在创建该类的对象的时候他们将被调用,如果没有定义构造方法,Java 编译器会提供一个默认构造方法。 创建一个对象的时候,至少调用一个构造方法。
2. 构造方法可以带参,也可以不带参,参数可以有多个。
3. 通过 new 关键字将类实例化成对象,而 new 后面跟的就是构造方法。于是可以知道 new + 构造方法 可以创建一个新的对象。
## 实验 54 工作方法调用
### 实验项目
* 工作方法调用
### 实验需求
1. 调用工作方法
### 实验内容
1. 在新建对象实例时,需要为对象实例设置一个对象名,就像这样
Object object=new Object();
那么变量 object 就真的是 Object 对象么,这里其实只是创建了一个 object 对象的引用。如果同学们学过 C 语言,这里就和指针一样,变量 object 保存的其实 Object 对象的引用,指向了 Object 对象。
object | ———> | Object |
---|---|---|
2. 再看下面的例子
public class Test(
public void test(){
System.out.println("我爱蓝桥");
}
public static void main(String args[]){
Test t=new Test();//实例化本类
t.test();//调用工作方法
}
)
### 实验结果
1.
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612496287476" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. 在建立类之后,有些类会声明一些方法,这些方法如何应用,就需要了解工作方法调用。
2. 普通方法的调用需要实例化对应类
## 实验 55 验证对象初始化过程实验
### 实验项目
* 验证对象初始化过程实验
### 实验需求
1. 理解类的构造方法及其意义。
2. 了解对象的基本初始化过程。
3. 理解方法重载及其意义。
### 实验内容
1. 首次创建某个对象时:
Dog dog = new Dog();
2. 首次访问某个类的静态方法或者静态字段时:
Dog.staticFields;
Java 解释器就会去找类的路径,定位已经编译好的 Dog.class 文件。
获得类的资源
然后 jvm 就会载入 Dog.class,生成一个 class 对象。这个时候如果有静态的方法或者变量,静态初始化动作都会被执行。这个时候要注意啦,静态初始化在程序运行过程中只会在 Class 对象首次加载的时候运行一次。这些资源都会放在 jvm 的方法区。
方法区又叫静态区,跟堆一样,被所有的线程共享。
方法区中包含的都是在整个程序中永远唯一的元素,包含所有的 class 和 static 变量。
初始化对象 Dog dog = new Dog()
* 第一次创建 Dog 对象先执行上面的一二步
* 在堆上为 Dog 对象分配足够的存储空间,所有属性和方法都被设置成默认值(数字为 0,字符为 null,布尔为 false,而所有引用被设置成 null)
* 执行构造函数检查是否有父类,如果有父类会先调用父类的构造函数,这里假设 Dog 没有父类,执行默认值字段的赋值即方法的初始化动作。
* 执行构造函数。
### 实验总结
1. 在类被装载、连接和初始化,这个类就随时都可能使用了。对象实例化和初始化是就是对象生命的起始阶段的活动,在这里我们主要讨论对象的初始化工作的相关特点。
2. 初始化 顺序应该是. 父静态变量-->子静态变量-->父非静态变量-->父静态代码块-->父构造函数------>子非变量-->子静态代码块-->子构造函数
## 实验 56 构造方法重载实验
### 实验项目
* 构造方法重载实验
### 实验需求
1. 构造方法重载
### 实验内容
public class Test {
void f(int i) {
System.out.println("i=" + i);
}
void f(float f) {
System.out.println("f=" + f);
}
void f(String s) {
System.out.println("s=" + s);
}
void f(String s1, String s2){
System.out.println("s1+s2="+(s1+s2));
}
void f(String s, int i){
System.out.println("s="+s+",i="+i);
}
public static void main(String[] args) {
Test test = new Test();
test.f(3456);
test.f(34.56f);
test.f("abc");
test.f("abc","def");
test.f("abc",3456);
}
}
### 实验结果
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612496672981" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. 方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。
2. 方法重载一般用于创建一组任务相似但是参数不同的方法。
3.
方法重载有以下几种规则:
* 方法中的参数列表必须不同。比如:参数个数不同或者参数类型不同。
* 重载的方法中允许抛出不同的异常。
* 可以有不同的返回值类型,但是参数列表必须不同。
* 可以有不同的访问修饰符。
## 实验 57 声明并封装宠物类
### 实验项目
* 声明并封装宠物类
### 实验需求
1. 声明并封装宠物类
### 实验内容
public class Pet {
private String name; //宠物名字
private int health; //宠物健康值
private int love; //宠物亲密度
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
if(health<0 || health>100) {
System.out.println("健康值应该在0至100之间,默认值为60。");
this.health=60;
return;
}
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
if(love<0 || love>100) {
System.out.println("亲密度应该在0至100之间,默认值为60。");
this.love=60;
return;
}
this.love = love;
}
}
### 实验结果
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210205-1612496988582" alt="图片.png" style="zoom:200%;" />
### 实验总结
1. 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
2. 属性创建getter/setter 方法
* 修改属性的可见性,在属性的前面添加修饰符 (private)。
* 对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值)方法,用于对私有属性的访问。
* 在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定。
3. 首先在类里要将属性前添加 private 修饰符。然后定义 getter和 setter 方法。
## 学生信息管理系统
### 实验项目
>* 运用数组和面向对象编程的知识实现学生信息管理系统
### 实验需求
>1. 增加一个学生:学号(长度为6位)、姓名、身份证号(18位)、性别(男/女)、年龄(1-100)
>2. 删除一个学生
>3. 修改一个学生的信息(姓名、身份证号、性别、年龄)
>4. 查询一个学生的信息
>5. 查询所有学生的信息
### 实验内容
>1. 根据需求创建Student类和StuMgr类 ;
>2. 将程序运行结果关键步骤截图
##
* Student类
####
package org.lanqiao.stumgr;
/
@java bean: Student
@author mhd
@Date 2021年1月28日 下午6:35:09
@version 1.0.0
*/
public class Student {
//1. 成员变量
/* 学号(长度为6位) /
private String no;
/ 姓名 */
private String name;
/ 身份证号(18位) */
private String idCard;
/* 性别(男/女) /
private String sex;
/ 年龄(1-100) */
private int age;
//2. 构造器
public Student() {}
/**
* @param no
* @param name
* @param idCard
* @param sex
* @param age
*/
public Student(String no, String name, String idCard, String sex, int age) {
super();
this.no = no;
this.name = name;
this.idCard = idCard;
this.sex = sex;
this.age = age;
}
//3. getter/setter
/**
* @return the no
*/
public String getNo() {
return no;
}
/**
* @param no the no to set
*/
public void setNo(String no) {
this.no = no;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the idCard
*/
public String getIdCard() {
return idCard;
}
/**
* @param idCard the idCard to set
*/
public void setIdCard(String idCard) {
this.idCard = idCard;
}
/**
* @return the sex
*/
public String getSex() {
return sex;
}
/**
* @param sex the sex to set
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
//4. toString
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Student [no=" + no + ", name=" + name + ", idCard=" + idCard + ", sex=" + sex + ", age=" + age + "]";
}
}
##
* StuMgr类
package org.lanqiao.stumgr;
import java.util.Scanner;
/*
@学生管理系统类
@author mhd
@Date 2021年1月28日 下午6:34:57
@version v3.0.0 – 数组+对象
/
public class StuMgr {
static Scanner input = new Scanner(System.in);
/* 学生的数量 /
public static int stuLength = 0;
public static Student[] stus = new Student[10];
public static void main(String[] args) {
while(true) {
StuMgr.showMenu();
System.out.println("请选择: ");
int sel = input.nextInt();
if(sel < 0 || sel > 5) {
System.out.println("选择有误,请重新输入(0-5)!");
}else {
switch(sel) {
case 1:
addStu();
break;
case 2:
deleteStu();
break;
case 3:
updateStu();
break;
case 4:
selectStu();
break;
case 5:
listAll();
break;
case 0:
exitStuMgrSys();
break;
}
}
}
}
/**
* @增加一个学生
*/
public static void addStu() {
Student student = new Student();
String no;
String name;
String idCard;
String sex;
int age;
while(true) {
System.out.println("请输入学号:");
no = input.next();
if(6 != no.length()) {
System.out.println("学号有误,必须是6位!");
continue;
}
if(getStuByNo(no) != null) {
System.out.println("该学号(学生)已存在!");
}else {
student.setNo(no);
break;
}
}
System.out.println("请输入姓名:");
name = input.next();
student.setName(name);
while(true) {
System.out.println("请输入身份证:");
idCard = input.next();
if(18 != idCard.length()) {
System.out.println("身份证号有误,必须是18位!");
continue;
}else {
student.setIdCard(idCard);
break;
}
}
while(true) {
System.out.println("请输入性别:");
sex = input.next();
if(sex.equals("男") || sex.equals("女")) {
student.setSex(sex);
break;
}else {
System.out.println("性别有误,必须是男或女!");
continue;
}
}
while(true) {
System.out.println("请输入年龄:");
age = input.nextInt();
if(age >= 1 && age <= 100) {
student.setAge(age);
break;
}else {
System.out.println("年龄有误,必须是1-100!");
continue;
}
}
stus[stuLength] = student;
stuLength++; //学生增加1
}
/**
* 删除一个学生
*/
public static void deleteStu() {
System.out.println("请输入待删除学生的学号:");
String stuNo = input.next();
Student stu = StuMgr.getStuByNo(stuNo);
if(null == stu) {
System.out.println("学号输入有误,该生不存在");
}else {
stu = stus[stuLength - 1];
stuLength--; //学生数量-1
System.out.println("删除成功!");
}
}
/**
* @修改一个学生
*/
public static void updateStu() {
Student stu = null;
while(true) {
System.out.println("请输入待修改学生的学号:");
String stuNo = input.next();
stu = StuMgr.getStuByNo(stuNo);
if(null == stu) {
System.out.println("学号有误,该学生不存在!");
continue;
}else {
break;
}
}
System.out.println("姓名:" + stu.getName());
System.out.println("请输入新的姓名:");
String name = input.next();
stu.setName(name);
while(true) {
System.out.println("身份证号:" + stu.getIdCard());
System.out.println("请输入新的身份证号:");
String idCard = input.next();
stu.setIdCard(idCard);
if(18 != idCard.length()) {
System.out.println("身份证号有误,必须是18位!");
continue;
}else {
break;
}
}
while(true) {
System.out.println("性别:" + stu.getSex());
System.out.println("请输入新的性别:");
String sex = input.next();
stu.setSex(sex);
if(sex.equals("男") || sex.equals("女")) {
break;
}else {
System.out.println("性别有误,必须是男或女!");
continue;
}
}
while(true) {
System.out.println("年龄:" + stu.getAge());
System.out.println("请输入新的年龄:");
int age = input.nextInt();
stu.setAge(age);
if(age >= 1 && age <= 100) {
break;
}else {
System.out.println("年龄有误,必须是1-100!");
continue;
}
}
}
/**
* @查询一个学生的信息
*/
public static void selectStu() {
Student stu;
while(true) {
System.out.println("请输入待查询学生的学号:");
String stuNo =input.next();
stu = StuMgr.getStuByNo(stuNo);
if(null == stu) {
System.out.println("查无此人!");
}else {
break;
}
}
System.out.println("学号:" + stu.getNo());
System.out.println("姓名:" + stu.getName());
System.out.println("身份证号:" + stu.getIdCard());
System.out.println("性别:" + stu.getSex());
System.out.println("年龄:" + stu.getAge());
}
/**
* @查询所有学生的信息
*/
public static void listAll() {
for(int i = 0; i < stuLength; i++) {
System.out.println("学号:" + stus[i].getNo());
System.out.println("姓名:" + stus[i].getName());
System.out.println("身份证号:" + stus[i].getIdCard());
System.out.println("性别:" + stus[i].getSex());
System.out.println("年龄:" + stus[i].getAge());
System.out.println();
}
}
/**
* @退出系统
*/
public static void exitStuMgrSys() {
System.exit(1);
}
/**
* @通过学号获取该该生
* @param no
* @return null
*/
public static Student getStuByNo(String no) {
for(int i = 0; i < stuLength; i++) {
if(no.equals(stus[i].getNo())) {
return stus[i];
}
}
return null;
}
/** 系统菜单 */
public static void showMenu() {
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("| 0. 退出系统 |");
System.out.println("-------------------------");
}
}
### 实验结果
1. 功能1增加学生成功
###
这里我通过在文本编辑器里实现写好内容,并且每个输入要素都换号行,所以可以一次性输入一个学生,结果显示没有报错,所以增加学生功能暂时算是成功了

###
然后增加学生时的校验也成功了


##
2. 功能5查询所有学生信息成功
###
接下来我再来测试一下功能5查询所有学生信息

##
3. 功能4查询一个学生信息成功
###
从结果来看,功能5也成功了, 然后是功能4查询一个学生信息

##
3. 功能3修改一个学生信息成功
###
再是功能3修改一个学生

然后再查询所以学生信息看看
###

###
还有就是修改学生时的校验功能也成功了

##
4. 功能2删除一个学生成功
###
然后是功能2删除一个学生并查询所以学生信息看一看

# (七)包和访问权限
## 实验 58 声明包实验
### 实验项目
声明包实验
### 实验需求
1. 定义包语法:
2. 使用 import 关键字。比如要导入包 `com.shiyanlou `下 `People `这个类,`import com.shiyanlou.People;`。同时如果` import com.shiyanlou.*;` 这是将包下的所有文件都导入进来,`*` 是通配符。
包的命名规范是全小写字母拼写。
### 实验内容
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
* 包采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。
* 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
[代码]
package 包名
//注意:必须放在源程序的第一行,包名可用"."号隔开
//在定义文件夹的时候利用"/"来区分层次
//包中用"."来分层
package com.shiyanlou.java
## 实验 59 引用包实验
### 实验项目
* 引用包实验
### 实验需求
1. 编写程序KY6_1.java,在源程序中,首先声明使用的包名 Mypackage,然后创建KY6_1类,该类具有计算今年的年份,可以输出一个带有年月日的字符串的功能。
copy
### 实验内容
1. 自定义包的声明方式
<自定义包名>
声明包语句必须添加在源程序的第一行,表示该程序文件声明的全部类都属于这个包。
2. 创建自定义包 Mypackage
在存放源程序的文件夹中建立一个子文件夹 Mypackage。
3. 在包中创建类。
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210129-1611912141740" alt="image.png" style="zoom:200%;" />
实验结果
1.

2. 包名成为类名的一部分,正如我们前面讨论的一样。
3. 包名必须与相应的字节码所在的目录结构相吻合。
## 实验 60 访问权限修饰符实验
### 实验项目
访问权限修饰符实验
### 实验需求
* Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。
1. 默认访问修饰符-不使用任何关键字
2. 私有访问修饰符-private
3. 公有访问修饰符-public
4. 受保护的访问修饰符-protected
### 实验内容
1. cd-命令能退回上一目录
2.

3. 使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。
>如下例所示,变量和方法的声明可以不使用任何修饰符。
>`String version = "1.5.1"; boolean processOrder() { return true; } `
4. getter和setter方法格式例子:

5. 被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。
* 如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。
* 由于类的继承性,类所有的公有方法和变量都能被其子类继承。
6. Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。
7. protected 需要从以下两个点来分析说明:
* 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
* 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
8. protected 可以修饰数据成员,构造方法,方法成员,不能修饰类(内部类除外)。
## 实验 61 static修饰变量实验
### 实验项目
* static修饰变量实验
### 实验需求
static修饰变量
### 实验内容
1. static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。
2. 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
3.
* 1) 静态成员不需要实例化 ,可以直接使用类名点取;
* 2) 如果不加static关键字 需要先创建对象,再用对象点取;
* 如果加上static关键字,上面的两种方法都可以使用
4.

5. <img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210129-1611912669833" alt="image.png" style="zoom:200%;" />
### 实验结果
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210129-1611912685649" alt="image.png" style="zoom:200%;" />
## 实验 62 static修饰方法实验
1. 静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
2.

3. 由于不实例化也可以调用,所以不能有 this
4. 不能访问非静态成员变量和非静态方法。但是非静态成员变量和非静态方法可以访问静态方法
### 实验项目
* static修饰方法实验
### 实验需求
* static修饰符用来创建类方法和类变量
### 实验内容
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210129-1611912781727" alt="image.png" style="zoom:200%;" />
### 实验结果
<img src="https://dn-simplecloud.shiyanlou.com/courses/uid1568799-20210129-1611912800406" alt="image.png" style="zoom:200%;" />
## 实验 63 static修饰块实验
### 实验项目
* static修饰块实验
### 实验需求
* static修饰符用来创建类方法和类变量
### 实验内容
Instance类
public class InstanceCounter {
static{
System.out.print("静态方法块优先运行");
}
}
Test类
public class Test1 {
public static void main(String[] args){
InstanceCounter i = new InstanceCounter();
}
}
### 实验结果

## 实验 64 静态引用实验
### 实验项目
* 静态引用实验
### 实验需求
1. 在开始import static, 导入指定的静态方法 在类中就可以直接使用。
### 实验内容
1.
Test1类
package org.lanqiao.projs.book1.chpt07.test10;
import static java.lang.System.err;
import static java.lang.System.out; //Java5的静态导入
public class Test1 {
public static void main(String[] args) {
out.println("Hello,World");
err.println("Error");
}
}
### 实验结果
1.

## 实验 65 导出jar文件实验
### 实验项目
* 导出jar文件实验
### 实验需求
1. 将已开发好的程序打成jar包。
### 实验内容
1. 在项目上,右键,选择Export

2.进入到下图界面,选择Java 下面的JAR file

3.选择项目,确认必要的文件是否选中,选择保存jar文件包的路径,如下图

4.完成步骤3之后,点击Next,进入如下图界面:

5.直接点Next,进入下面的界面:

6. 在Main class处选择相应的Main class, 再点Finish,jar文件包完成。

### 实验结果

* 在windows下面运行jar包,确认导出的jar包是否存在问题:
打开DOS命令:通过cd切换到本项目路径下,然后,输入java -jar xxx.jar。
如果输入的信息,和运行项目时,在控制台输出的信息一样,就表示jar文件包已经完成。
在windows下面运行jar包,确认导出的jar包是否存在问题:
打开DOS命令:通过cd切换到本项目路径下,然后,输入java -jar xxx.jar。
如果输入的信息,和运行项目时,在控制台输出的信息一样,就表示jar文件包已经完成。

# (八)继承和多态
## 实验 66 super关键字实验
### 实验项目
* super关键字实验
### 实验需求
1. 通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
### 实验内容
1.
Test类
/**
-
*/
package org.lanqiao.projs.book1.chpt08.section01;
/*
@ super关键字实验
@author mhd
@Date Jan 31, 2021 6:44:48 PM
@version 1.0.0
/
class Animal {
void eat() {
System.out.println("animal: eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog: eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类的方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
### 实验结果

### 实验总结
1. 访问父类的属性 `super.属性名`。
2. 访问父类的方法 `super.bark()`。
3. 子类构造方法需要调用父类的构造方法时,在子类的构造方法体里最前面的位置:`super()`。
## 实验 67 方法重写实验
### 实验项目
* 方法重写实验
### 实验需求
* 方法重写,重写的方法一定要与原父类的方法语法保持一致,比如返回值类型,参数类型及个数,和方法名都必须一致
### 实验内容
1. Animal类
pulbic class Animal {
//类方法
public void bark(){
System.out.println("动物叫!");
}
}
2. Dog类
public class Dog extends Animal{
//重写父类的bark方法
public void bark(){
System.out.println("汪!汪!汪!");
}
}
3. Test类
public class Test{
public static void main(String args[]){
Animal a = new Animal(); //Animal对象
Dog d =new Dog(); //Dog对象
Animal b = new Dog(); //Dog对象,向上转型为Animal类型,
//具体会在后面的内容进行详解
a.bark(); //执行Animal类的方法
d.bark(); //执行Dog类方法
b.bark(); //执行Dog类的方法
}
}
### 实验结果

### 实验总结
1. 当然在方法重写时要注意,重写的方法一定要与原父类的方法语法保持一致,比如返回值类型,参数类型及个数,和方法名都必须一致。
## 实验 68 this关键字实验
### 实验项目
* this关键字实验
### 实验需求
1. this 关键字代表当前对象。使用 this.属性 操作当前对象的属性,this.方法 调用当前对象的方法。
### 实验内容
1. Car类的getter和setter方法
public class Car{
private int age;
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
### 实验总结
1. 用 private 修饰的属性,必须定义 getter 和 setter 方法才可以访问到 (Eclipse 和 IDEA 等 IDE 都有自动生成 getter 和 setter 方法的功能);
2. 当成员变量和局部变量之间发生冲突时,在属性名前面添加了 this关键字。 此时就代表将一个参数的值赋给当前对象的属性。同理 this 关键字可以调用当前对象的方法。
3. 用 private 修饰的属性,必须定义 getter 和 setter 方法才可以访问到 (Eclipse 和 IDEA 等 IDE 都有自动生成 getter 和 setter 方法的功能)。
## 实验 69 属性覆盖实验
### 实验项目
* 属性覆盖实验
### 实验需求
1. 属性覆盖,类的属性在继承的情况下,假如父类和子类存在同名的属性会出现什么情况呢?这就是属性的覆盖。
### 实验内容
1. Test类
public class Test{
void f(int i){
System.out.println("i=" + i);
}
void f(float f){
System.out.println("f=" + f);
}
void f(String s){
System.out.println("s=" + s);
}
void f(String s1, String s2){
System.out.println("s1+s2=" + (s1+s2));
}
void f(String s, int i){
System.out.println("s=" + s + ",i=" + i);
}
public static void main(String[] args){
Test test = new Test();
test.f(3456);
test.f(34.56f);
test.f("abc");
test.f("abc","def");
test.f("abc",3456);
}
}
### 实验结果
1.

### 实验总结
1. 方法中的参数列表必须不同。比如:参数个数不同或者参数类型不同。
2. 重载的方法中允许抛出不同的异常。
3. 可以有不同的返回值类型,但是参数列表必须不同。
4. 可以有不同的访问修饰符。
## 实验 70 多态的四种体现实验
### 实验项目
* 多态的四种体现实验
### 实验需求
1. 向上转型
### 实验内容
1. Test类
class Animal {
//父类方法
public void bark(){
System.out.println("动物叫!");
}
}
class Dog extends Animal{
//子类重写父类的bark方法
public void bark(){
System.out.println("汪!汪!汪!");
}
//子类自己的方法
public void dogType(){
System.out.println("这是什么品种的狗?");
}
}
public class Test{
public static void main(String[] args){
Animal a = new Animal();
Animal b = new Dog();
Dog d = new Dog();
a.bark();
b.bark();
}
### 实验结果

在这里,由于 b 是父类的引用,指向子类的对象,因此不能获取子类的方法(dogType() 方法), 同时当调用 bark() 方法时,由于子类重写了父类的 bark() 方法,所以调用子类中的 bark() 方法。
### 实验总结
1. 多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。多态也称作动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
2. Java 实现多态有三个必要条件:继承、重写和向上转型(即父类引用指向子类对象)
3. 向上转型:
Animal a = new Animal(); //a是父类的引用指向的是本类的对象
Animal b = new Dog(); //b是父类的引用指向的是子类的对象
在这里,可以认为由于 Dog 继承于 Animal,所以 Dog 可以自动向上转型为 Animal,所以 b 是可以指向 Dog 实例对象的。
## 实验 71 宠物类的抽象和多态调用
### 实验项目
* 宠物类的抽象和多态调用
### 实验需求
1. 向上转型
### 实验内容
1. Test类
class Animal {
//父类方法
public void bark(){
System.out.println("动物叫!");
}
}
class Dog extends Animal{
//子类重写父类的bark方法
public void bark(){
System.out.println("汪!汪!汪!");
}
//子类自己的方法
public void dogType(){
System.out.println("这是什么品种的狗?");
}
}
public class Test{
public static void main(String[] args){
Animal a = new Animal();
Animal b = new Dog();
Dog d = new Dog();
a.bark();
b.bark();
}
### 实验结果

在这里,由于 b 是父类的引用,指向子类的对象,因此不能获取子类的方法(dogType() 方法), 同时当调用 bark() 方法时,由于子类重写了父类的 bark() 方法,所以调用子类中的 bark() 方法。
### 实验总结
1. 多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。多态也称作动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
2. Java 实现多态有三个必要条件:继承、重写和向上转型(即父类引用指向子类对象)
3. 向上转型:
Animal a = new Animal(); //a是父类的引用指向的是本类的对象
Animal b = new Dog(); //b是父类的引用指向的是子类的对象
在这里,可以认为由于 Dog 继承于 Animal,所以 Dog 可以自动向上转型为 Animal,所以 b 是可以指向 Dog 实例对象的。
4. 向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法,也会覆盖与父类中相同的方法——重写(方法名,参数都相同)。
5. Java 中多态的实现方式:继承父类进行方法重写,抽象类和抽象方法,接口实现。
# (九)抽象类和接口
## 实验 72 抽象类的声明实验
### 实验项目
* 抽象类的声明实验
### 实验需求
1. 抽象类的声明
### 实验内容
1. 在 home/project/ 目录下创建一个抽象类 TelePhone.java。
2. 填写需要子类实现的抽象方法。
TelePhone类
//抽象类
public abstract class TelePhone {
public abstract void call(); //抽象方法,打电话
public abstract void message(); //抽象方法,发短信
}
### 实验总结
1. 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
2. 在定义抽象类类时,前面加上 abstract 关键字。
## 实验 73 抽象方法的重写实验
### 实验项目
* 抽象方法的重写实验
### 实验需求
### 实验内容
1. 在 home/project/ 目录下创建一个抽象类 TelePhone.java。
2. 填写需要子类实现的抽象方法。
TelePhone类
//抽象类
public abstract class TelePhone {
public abstract void call(); //抽象方法,打电话
public abstract void message(); //抽象方法,发短信
}
3. 构建子类,并实现抽象方法。新建一个 CellPhone.java。
CellPhone类
public class CellPhone extends TelePhone{
@Override
public void call(){
System.out.println("我可以打电话! ");
}
@Override
public void message(){
System.out.println("我可以发短信! ");
}
public static void main(String[] args){
CellPhone cp = new CellPhone();
cp.call();
cp.message();
}
}
4. 在 CellPhone 类添加 main 方法测试运行结果。
### 实验结果

### 实验总结
1. 如果想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
2. 如果一个类包含抽象方法,那么该类必须是抽象类。
3. 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
4. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
5. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
6. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
7. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
8. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
## 实验 74 匿名内部类的实验
### 实验项目
* 匿名内部类的实验
### 实验需求
1. 匿名内部类
### 实验内容
1. Outer类
//Outer.java
public class Outer{
public Inner getInner(final String name, String city){
return new Inner(){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "NewYork");
System.out.println(inner.getName());
}
}
interface Inner{
String getName();
}
### 实验结果

### 实验总结
1. 匿名内部类,顾名思义,就是没有名字的内部类。
2. 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。
3. 匿名内部类是不能加访问修饰符的。要注意的是,new 匿名类,这个类是要先定义的, 如果不先定义,编译时会报错该类找不到。
## 实验 75 接口的声明实验
### 实验项目
* 接口的声明实验
### 实验需求
1. 接口的声明
### 实验内容
1. 接口的声明语法格式如下:
修饰符 interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
Animal接口
//Animal.java
interface Animal{
//int x;
//编译错误,x需要初始化,因为是static final类型
int y = 5;
public void eat();
public void travel();
}
### 实验总结
1. 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
2. 接口用于描述类所具有的功能,而不提供功能的实现,功能的实现需要写在实现接口的类中,并且该类必须实现接口中所有的未实现方法。
3. 接口不能用于实例化对象。
4. 接口中方法只能是抽象方法、default 方法、静态方法。
5. 接口成员是 static final 类型。
6. 接口支持多继承。
## 实验 76 接口的重写实验
### 实验项目
* 接口的重写实验
### 实验需求
1. 接口的重写
### 实验内容
1. 多继承实现方式:
修饰符 interface A extends 接口1,接口2{
}
修饰符 class A implements 接口1,接口2{
}
2. Animal类
//Animal.java
interface Animal{
//int x;
//编译错误,x需要初始化,因为是static final类型
int y = 5;
public void eat();
public void travel();
}
3. Cat类
//Cat.java
public class Cat implements Animal{
public void eat(){
System.out.println("Cat eats");
}
public void travel(){
System.out.println("Cat travels");
}
public static void main(String[] args){
Cat cat = new Cat();
cat.eat();
cat.travel();
}
}
### 实验结果

### 实验总结
1. 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
2. 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
3. 类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
4. 一个类可以同时实现多个接口。
5. 一个类只能继承一个类,但是能实现多个接口。
6. 一个接口能继承另一个接口。
## 实验 77 宠物类的抽象类和接口
### 实验项目
* 宠物类的抽象类和接口
### 实验需求
1. 升级宠物商店 -------------instanceof:判断某对象是否实现了某个接口(boolean=obj instanceof 接口)。
### 实验内容
1. Shop类
interface Animal{
void eat();
}
interface Animal1 extends Animal{
void shout();
}
interface Animal2 extends Animal{
int getLeg();
}
class Cat implements Animal1, Animal2{
public void shout(){
System.out.println("喵喵~");
}
public int getLeg(){
//TODO Auto-generated method stub
return 4;
}
public void eat(){
//TODO Auto-generated method stub
System.out.println("我吃鱼.");
}
}
class Pig implements Animal1, Animal2{
public void shout(){
System.out.println("哼哼~,我是小猪,");
}
public int getLeg(){
//TODO Auto-generated method stub
return 4;
}
public void eat(){
//TODO Auto-generated method stub
System.out.println("我吃饲料.");
}
}
class Dog implements Animal1, Animal2{
public void shout(){
System.out.println("旺旺~,我是小狗,");
}
public int getLeg(){
//TODO Auto-generated method stub
return 4;
}
public void eat(){
//TODO Auto-generated method stub
System.out.println("我吃肉.");
}
}
class Fish implements Animal{
public void eat(){
System.out.println("我是小鱼,我吃蚯蚓.");
}
}
class Dolphin implements Animal1{
public void shout(){
System.out.println("吱吱~,我是海豚,");
}
public void eat(){
//TODO Auto-generated method stub
System.out.println("我吃鱼.");
}
}
public class Shop{
public Animal get(int choice){
if(choice==1){
return new Dog();
}else if(choice==2){
return new Pig();
}else if(choice==3){
return new Cat();
}else if(choice==4){
return new Dolphin();
}else {
return new Fish();
}
//或者
// switch(choice){
// case 1:
// return new Dog();
// break;
// case 2:
// return new Pig();
// break;
// case 3:
// return new Cat();
// break;
// case 4:
// return new Dolphin();
// break;
// default:
// return new Fish();
// break;
// }
}
}
2. Test类
//测试类
import java.util.Scanner;
public class Test{
public static void main(String[] args){
System.out.println("请选择您要领养的小动物: ");
System.out.println("1,小狗");
System.out.println("2,小猪");
System.out.println("3, 小猫");
System.out.println("4, 海豚");
System.out.println("5,鱼");
Scanner sc = new Scanner(System.in);
int choice = sc.nextInt();
Shop s = new Shop();
Animal a = s.get(choice);
//判断并输出
if((a instanceof Animal1)&&(a instanceof Animal2)){
a.eat();
((Animal1)).shout();
System.out.println("我有" + ((Animal2)a).getLeg() + "条腿");
}else if(a instanceof Animal1){
((Animal1)a).shout();
a.eat();
System.out.println("我没有腿");
}else{
a.eat();
System.out.println("我不会叫,我没有腿哦");
}
}
}
### 实验结果

### 实验总结
1. 在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
## 实验 78 宠物商店管理系统
### 实验项目
* 宠物商店管理系统
### 实验需求
1. 面向对象阶段综合练习
### 实验内容
1. Pet类
/**
-
*/
package org.lanqiao.projs.book1.chpt09.com.szxs.pet;
/*
@
@author mhd
@Date Feb 4, 2021 11:36:04 AM
@version 1.0.0
/
public class Pet {
private String name; //宠物名字
private int health; //宠物健康
private int love; //宠物亲密度
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the health
*/
public int getHealth() {
return health;
}
/**
* @param health the health to set
*/
public void setHealth(int health) {
if(health < 0 || health > 100) {
System.out.println("健康值应该在0至100之间,默认值为60.");
this.health = 60;
this.health = health;
return;
}
this.health = health;
}
/**
* @return the love
*/
public int getLove() {
return love;
}
/**
* @param love the love to set
*/
public void setLove(int love) {
if(love < 0 || love > 100) {
System.out.println("亲密度应该在0至100之间,默认值为60.");
this.love = 60;
}
this.love = love;
}
}
2. Dog类
/**
-
*/
package org.lanqiao.projs.book1.chpt09.com.szxs.pet;
/*
@
@author mhd
@Date Feb 4, 2021 11:41:19 AM
@version 1.0.0
/
public class Dog extends Pet {
private String strain; //狗狗品种
/**
* @return the strain
*/
public String getStrain() {
return strain;
}
/**
* @param strain the strain to set
*/
public void setStrain(String strain) {
this.strain = strain;
}
public void print() {
System.out.println("宠物的自白:");
System.out.println("我的名字叫:" + super.getName());
System.out.println("健康值是:" + super.getHealth());
System.out.println("和主人的亲密度是:" + super.getLove());
System.out.println("我是一只" + strain + ".");
}
}
3. Penguin类
/**
-
*/
package org.lanqiao.projs.book1.chpt09.com.szxs.pet;
/*
@
@author mhd
@Date Feb 4, 2021 11:46:43 AM
@version 1.0.0
/
public class Penguin extends Pet {
private String sex; //性别
/**
* @return the sex
*/
public String getSex() {
return sex;
}
/**
* @param sex the sex to set
*/
public void setSex(String sex) {
this.sex = sex;
}
public void print() {
System.out.println("宠物的自白:");
System.out.println("我的名字叫:" + super.getName());
System.out.println("健康值是:" + super.getHealth());
System.out.println("和主人的亲密度是:" + super.getLove());
System.out.println("我的性别是:" + sex + ".");
}
}
4. TestPet类
/**
-
*/
package org.lanqiao.projs.book1.chpt09.com.szxs.pet;
import java.util.Scanner;
/*
@
@author mhd
@Date Feb 4, 2021 11:48:46 AM
@version 1.0.0
/
//封装宠物类
public class TestPet {
public static void main(String[] args) {
Dog dog = new Dog();
Penguin pg = new Penguin();
Scanner input = new Scanner(System.in);
System.out.println("欢迎来到宠物店!");
System.out.println("请选择要领养的宠物类型:(1.狗狗 ; 2.企鹅)");
switch(input.nextInt()) {
case 1:
System.out.println("请输入要领养的狗狗名字:");
dog.setName(input.next());
System.out.println("请要领养的宠物类型:(1.聪明的拉布拉多犬; 2.酷酷的雪纳瑞)");
String strain = input.nextInt() == 1?"聪明的拉布拉多犬":"酷酷的雪纳瑞";
dog.setStrain(strain);
System.out.println("请输入要狗狗的健康值:");
dog.setHealth(input.nextInt());
System.out.println("请输入狗狗亲密度:");
dog.setLove(input.nextInt());
dog.print();
break;
case 2:
System.out.println("请输入要领养的企鹅名字:");
dog.setName(input.next());
System.out.println("请要领养的企鹅性别:(1.Q仔; 2.Q妹)");
String sex = input.nextInt() == 1?"Q仔":"Q妹";
dog.setStrain(sex);
System.out.println("请输入要的健康值:");
dog.setHealth(input.nextInt());
System.out.println("请输入企鹅亲密度:");
dog.setLove(input.nextInt());
dog.print();
break;
}
}
}
### 实验结果

### 实验总结
1. 综合使用面向对象相关知识
# (十)数据结构
## 实验 79 二分法查找实验
## 实验 80 插入排序实验
## 实验 81 图书馆书籍管理系统