*本文是博主对Java各种实验的再整理与详解,除了代码部分和解析部分,一些题目还增加了拓展部分(⭐)。拓展部分不是实验报告中原有的内容,而是博主本人自己的补充,以方便大家额外学习、参考。
目录
(1)分别用do-while和for循环计算1+1/2!-1/3!+1/4!-1/5!…的前20项之和。
(2)求1000以内的完全数(一个数等于它的因子之和称为完全数)
(1)求六边形面积,六边形面积可以通过下面公式计算(s是边长)
(2)实现会员注册,要求用户名长度不小于3,密码长度不小于6,若不满足需有提示信息,提示输入有误;注册时两次输入密码必须相同(字符串)
(3)找出两个分教最高的学生:编写程序,提示输入学生的个数、每个学生的名字及其分数,最后显示获得最高分的学生和第二高分的学生
(4)定义一维数组并初始化,通过键盘任意输入一个数,查找该数是否存在(结果返回下标值)
(5)编写一个程序,将二维数组a转置后存入数组b(所谓转置就是行列互换)
一、实验目的
1、掌握变量的命名是否符合Java关于标识符的命名规则。
2、掌握8种基本数据类型和3种类型转换的方法。
3、熟悉各种运算符的优先级和结合方向。
4、掌握控制结构的3种语句。
5、掌握Java输入数据的基本方法;Scanner。
6、掌握方法的定义、调用格式,理解方法调用和参数传递的原理,掌握方法的重载,理解方法的递归调用。
7、掌握数组的定义、动态和静态初始化格式,数组的遍历和应用,Array工具类的使用。
二、实验内容
1、实验题目:基本语法
(1)求任意输入的10个数的和。
源代码:
常规方式for循环输入并求和。该题逻辑非常基础,不作过多说明。对于Scanner不了解的同学可以查看该文档:Scanner
import java.util.Scanner;
public class S2_1_1 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in); //创建Scanner对象
int sum = 0;
for(int i = 1; i <= 10; i++) { //连续输入10个数
System.out.println("请输入第"+i+"个数:");
int n = reader.nextInt();
sum += n; //累加求和
}
reader.close(); //记得关闭
System.out.println(sum);
}
}
列出测试数据和实验结果截图:
(2)实验题目:获取三个整数中的最大值(用三元运算符)
源代码:
代码1:直接使用三目运算符。
import java.util.Scanner;
public class S2_1_2 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int a = reader.nextInt();
int b = reader.nextInt();
int c = reader.nextInt();
int temp = (a > b) ? a : b;
//若a大于b,则三目运算符表达式值为a,否则为b
int max = (temp > c) ? temp : c;
//若temp大于c,则三目运算符表达式值为temp,否则为c
System.out.println("max = " + max);
}
}
代码2:将求最大值的程序封装成方法,通过调用方法获取最大值。
import java.util.Scanner;
public class S2_1_2 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int a = reader.nextInt();
int b = reader.nextInt();
int c = reader.nextInt();
int temp = max(a,b); //调用方法
int max = max(temp,c);
System.out.println("max = " + max);
}
public static int max(int x, int y){
return x > y ? x : y;
}
}
列出测试数据和实验结果截图:
⭐拓展-通过泛型类获取任意类型的三个数的最大值
提问:如果想要比较的对象不仅是整数,而是任意的基本数据类型,那该怎么办呢?
当然,我们可以采用方法重载,枚举多种情况、写多个方法。但这样依旧有些费劲,毕竟光是可以比较的数值类型就有7种,更何况可能还需要比较自定义类型。这时,我们可以借助泛型来实现。详解部分可以查阅下面这篇文章:
🔗Java初识泛型 | 如何通过泛型类/泛型方法获取任意类型的三个数的最大值?
2、实验题目:选择结构的应用
(1)键盘录入月份的值,输出对应的季节
源代码:
用switch或if来进行分支判断都是简便且可行的,这里给出用if实现的代码。
import java.util.Scanner ;
public class S2_2_1 {
public static void main(String[] args){
Scanner sc = new Scanner(System.in) ;
System.out.println("请输入一个月份的值:") ;
int month = sc.nextInt() ;
if(month > 12 || month < 1){
System.out.println("您输入的月份不合法") ;
}else if(month >= 3 && month <= 5){
System.out.println("春季") ;
}else if(month >= 6 && month <= 8){
System.out.println("夏季") ;
}else if(month >= 9 && month <= 11){
System.out.println("秋季") ;
}else{
System.out.println("冬季") ;
}
}
}
列出测试数据和实验结果截图:
(2)输入年份和月份,输出该年月的天数
import java.util.Scanner;
public class S2_2_2 {
public static void main(String[] args){
Scanner s =new Scanner (System.in);
System.out.println("请输入年份:");
int year= s.nextInt();
System.out.println("请输入月份:");
int month= s.nextInt();
int day = 0;
switch (month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
day = 31;
break;
case 4:
case 6:
case 9:
case 11:
day = 30;
break;
case 2:
if(year%4 == 0 && year%100 != 0 || year%400==0){
day=29;
}else{
day=28;
}
break;
}
System.out.println(year+"年"+month+"月有"+day+"天");
}
}
⭐拓展-输入年、月、日,输出这天是该年的第几天
我们写分支结构时,当发现既可以使用switch-case(同时switch中表达式的取值情况不太多),又可以使用if-else时,我们可以优先选择使用switch-case,因为switch-case的执行效率稍高。
import java.util.Scanner;
public class YearDayTest {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
System.out.println("请输入year:");
int year = reader.nextInt();
System.out.println("请输入month:");
int month = reader.nextInt();
System.out.println("请输入day:");
int day = reader.nextInt();
//定义一个变量来保存天数
int sumDays = 0;
//不加break,达到逐月累加的效果
switch(month) {
case 12:
sumDays += 30; //加的是上个月也就是11月的天数
case 11:
sumDays += 31;
case 10:
sumDays += 30;
case 9:
sumDays += 31;
case 8:
sumDays += 31;
case 7:
sumDays += 30;
case 6:
sumDays += 31;
case 5:
sumDays += 30;
case 4:
sumDays += 31;
case 3:
if(year%4 == 0 && year%100 != 0 || year%400==0){
sumDays += 29;
}else{
sumDays += 28;
}
case 2:
sumDays += 31;
case 1:
sumDays += day; //最后加上当月的天数
}
System.out.println(year + "年" + month + "月" + day + "日是当年的第" + sumDays + "天" );
}
}
(3)出租车计费问题
• KF市的出租车计费方式为:起步2公里内5元,2公里以上每公里收费1.3元,9公里以上每公里收费2元,燃油附加费1元。
• 编写程序,输入公里数,计算出所需的出租车费用。
源代码:
import java.util.Scanner;
public class S2_2_3 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
double price = 0.0;
System.out.println("请输入里程数:");
double mile = reader.nextDouble();
if(mile <= 2 && mile > 0){
price = 5;
} else if (mile > 2 && mile <= 9) {
price = (mile-2) * 1.3 + 5;
} else if (mile > 9) {
price = 5 + 7 * 1.3 + (mile-9) * 2 + 1;
}
System.out.println("价格为:" + price + "元。");
}
}
列出测试数据和实验结果截图:
3、实验题目:循环结构的应用
(1)分别用do-while和for循环计算1+1/2!-1/3!+1/4!-1/5!…的前20项之和。
本题是基础的累加、阶乘的运用。较为基础,故不多作说明。
源代码:
for循环实现
public class S2_3_1 {
public static void main(String[] args) {
double sum = 0;
double fact = 1;
for(int i = 1; i <= 20; i++){
fact *= i; //求每项中的阶乘
sum += 1/fact; //将各项累加
}
System.out.println(sum);
}
}
do-while循环实现
public class S2_3_1 {
public static void main(String[] args) {
double sum = 0;
double fact = 1;
int i = 1;
do {
fact *= i++;
sum += 1/fact;
}while(i <= 20);
System.out.println(sum);
}
}
列出测试数据和实验结果截图:
(2)求1000以内的完全数(一个数等于它的因子之和称为完全数)
通过试除法,找到数n所有的因子并相加,最后作判断即可。
源代码:
public class S2_3_2 {
//判断是否是完全数的方法
public static boolean isPerfectNumber(int n){
//一个数等于它的因子之和称为完全数
int sum = 0; //用于记录因子之和
for(int i = 1; i <= n/2; i++){ //试除法寻找因子
if(n%i == 0){
sum += i; //此处i即n的因子
}
}
//比较因子之和与这个数本身是否相等
if(sum == n){
return true;
}else{
return false;
}
}
public static void main(String[] args) {
for(int n = 6; n <= 1000; n++){
if(isPerfectNumber(n)){
System.out.println(n);
}
}
}
}
(3)微信中的一个拿鸡蛋问题
一筐鸡蛋,1个1个拿,正好拿完。
2个2个拿,还剩1个。
3个3个拿,正好拿完。
4个4个拿,还剩1个。
5个5个拿,还差1个。
6个6个拿,还剩3个。
7个7个拿,正好拿完。
8个8个拿,还剩1个。
9个9个拿,正好拿完。
问:筐里最少有多少鸡蛋?
这个问题非常简单,但必须注意细节:5个5个拿时,是还“差”一个,而不是还“剩”一个。假设有4个鸡蛋,那么此时5个5个拿,就是还差一个的情景。因此,此时的鸡蛋数加上差的1个,恰好是5的倍数。
源代码:
public class S2_3_3 {
public static void main(String[] args) {
int i = 0;
while(true) {
i++;
if(i%2==1 && i%3==0 && i%4==1 && (i+1)%5==0 && i%6==3 && i%7==0 && i%8==1 && i%9==0){
System.out.println(i);
break;
}
}
}
}
列出测试数据和实验结果截图:
5、实验题目:课后作业题
(1)求六边形面积,六边形面积可以通过下面公式计算(s是边长)
注:使用Math类中的方法计算tan值。
源代码:
public class Test {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
double s = reader.nextDouble();
System.out.println(getArea(s));
}
public static double getArea(double s){
double x1 = 6*Math.pow(s,2);
double x2 = 4*Math.tan(Math.PI/6);
return x1/x2;
}
}
列出测试数据和实验结果截图:
(2)实现会员注册,要求用户名长度不小于3,密码长度不小于6,若不满足需有提示信息,提示输入有误;注册时两次输入密码必须相同(字符串)
本题的关键是字符串的输入与两个字符串之间的比较方法equals的运用。注意,若要求忽略大小写比较,则用到equalsIgnoreCase()方法。
源代码:
import java.util.Scanner;
public class S2_5_2 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = reader.next();
while(userName.length() < 3) {
System.out.println("用户名长度不可小于3!请重新输入:");
userName = reader.next();
}
System.out.println("请输入密码:");
String password = reader.next();
while(password.length() < 6) {
System.out.println("密码长度不可小于6!请重新输入:");
password = reader.next();
}
System.out.println("请确认密码:");
String confirmPassword = reader.next();
if(confirmPassword.equals(password)) {
System.out.println("注册成功!");
}else {
System.out.println("两次密码不一致!");
}
}
}
列出测试数据和实验结果截图:
(3)找出两个分教最高的学生:编写程序,提示输入学生的个数、每个学生的名字及其分数,最后显示获得最高分的学生和第二高分的学生
方法一:直接求解
该代码没有用到面向对象的思想,编程思路是分别创建两个数组:分数数组与姓名数组进行分开存储。通过排序代码,以分数为标准对姓名数组进行排序,最后输出前两项的姓名。
源代码:
import java.util.Scanner;
public class S2_5_3 {
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
System.out.println("请输入学生个数");
int num = reader.nextInt();
//创建分数数组和姓名数组进行分开存储
int[] score = new int[num];
String[] name = new String[num];
for(int i=0;i<num;i++)
{
System.out.println("请输入第"+(i+1)+"个学生的名字");
name[i]= reader.next();
System.out.println("请输入第"+(i+1)+"个学生的成绩");
score[i]=reader.nextInt();
}
//直接选择排序
for(int i=0;i<num;i++)
{
for(int j=i+1;j<num;j++)
{
if(score[i]<score[j])
{
String tmp = name[i];
name[i] = name[j];
name[j] = tmp;
}
}
}
System.out.println("第一名为:"+name[0]+"。第二名为:"+name[1]);
}
}
列出测试数据和实验结果截图:
方法二:面向对象
该方法是上一种方式的优化,核心思路是实现Student类,创建Student数组,并通过直接对Student数组排序的方式最高分的Student对象与第二高分的Student对象。
注意:由于数组排序的过程中设计对象的比较,因此需要重写compareTo()方法才能进行Student数组的排序。
源代码:
import java.util.Arrays;
import java.util.Scanner;
public class S2_5_3 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
System.out.println("请输入学生的个数:");
int num = reader.nextInt();
Student[] students = new Student[num];
System.out.println("请依次输入学生的姓名与分数:");
for (int i = 0; i < students.length; i++){
System.out.println("学生"+(i+1));
String namei = reader.next(); //① 注意:写成.next()和.nextLine()在此有区别!
int scorei = reader.nextInt(); //②
students[i] = new Student(namei,scorei); //③
}
Arrays.sort(students); //④
System.out.println("获得最高分的学生是:" + students[0]);
System.out.println("获得第二高分的学生是:" + students[1]);
}
}
class Student implements Comparable<Student>{ //⑤
String name;
int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
//重写compareTo方法以数组排序
@Override
public int compareTo(Student o) {
return o.score-this.score;
}
//重写toString方法以打印对象
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
代码详解:
- 注释①处写成.next()和.nextLine()在此是有区别的。正确的方式应当是写成reader.next(); 一方面,.next()不会因为获取到上一个输入遗留下来的换行空白符而停止读取;另一方面,在用户实际输入的时候既可以将“姓名”和“分数”用换行隔开,也可以用空格等非换行符隔开。若写成.nextLine()。则会有上面的两个问题,会抛出InputMismatchException
通过调试可以知道,此时的字符串“小明”并没有被①代码接收,而是被②代码接收,①代码似乎被直接“跳过”了,类型不匹配因此异常 - ③处代码是创建新的Student对象。注意,在创建Student数组的时候,并没有实例化Student对象。数组没有初始化,因此Student数组内均为null。通过构造方法给Student对象的name和score赋值,并实例化数组中的元素。
- ④直接调用Arrays中的方法sort给数组中的元素进行排序。注意:该数组是引用类型,要实现排序必须重写Comparable或Comparator接口,并重写相应的比较方法,也就是指定比较的规则。
- ⑤处实现了Comparable接口。
列出测试数据和实验结果截图:
(4)定义一维数组并初始化,通过键盘任意输入一个数,查找该数是否存在(结果返回下标值)
方法一:数组中查找比对
源代码:
import java.util.Arrays;
import java.util.Scanner;
public class S2_5_44 {
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int[] array={0,1,2,3,4,5,6,7,8,9};
System.out.println("数组为:");
System.out.println(Arrays.toString(array));
System.out.println("请输入一个数:");
int num = reader.nextInt();
int i = 0;
for(; i < array.length; i++)
{
if(num == array[i])
{
break;
}
}
if(i < array.length){
System.out.println("该数存在,下标为" + i);
}else{
System.out.println("没有找到");
}
}
}
列出测试数据和实验结果截图:
方法二:将数组转变为字符串后,调用indexOf()方法
注意,将int数组转换为String,不能直接调用Arrays.toString()方法。因为该方法输出的内容并不完全是原数组中的,而是还有分隔符逗号、前后中括号[] 等。通过调试可以看到:
我们希望0下标的位置是"0",但这里却是"["。Java中没有库函数可以实现int数组直接转换成String的方法,因此我们需要自己实现一个myToString()来完成,逻辑也并不难。
源代码:
import java.util.Arrays;
import java.util.Scanner;
public class S2_5_4 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println("数组为:");
System.out.println(Arrays.toString(array));
System.out.println("请输入一个数:");
int num = reader.nextInt();
//将待查找的数转变成String类型,再查找
String strNum = String.valueOf(num);
//调用自己实现的方法,将int数组转变成String数组
String strArray = myToString(array);
//获取查找下标值
int ret = strArray.indexOf(strNum);
if(ret != -1){
System.out.println("找到了,下标是:" + ret);
} else {
System.out.println("没有找到");
}
}
//自己实现将int数组转换为String的方法
public static String myToString(int[] array) {
String str = "";
for (int x : array) {
str += x+"";
}
return str;
}
}
列出测试数据和实验结果截图:
方法三:调用Arrays工具类中的binarySearch()方法
如图,这是binarySearch()一个方法的api文档。通过文档可知,配合sort()方法,可以直接调用二分查找方法实现查找。
源代码:
import java.util.Arrays;
import java.util.Scanner;
public class S2_5_4 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println("数组为:");
System.out.println(Arrays.toString(array));
System.out.println("请输入一个数:");
int num = reader.nextInt();
Arrays.sort(array); //必须先进行排序
int ret = Arrays.binarySearch(array,num); //调用二分查找方法
if(ret < 0) {
System.out.println("没有找到!");
}else{
System.out.println("找到了,下标为" + ret);
}
}
}
(5)编写一个程序,将二维数组a转置后存入数组b(所谓转置就是行列互换)
例如:
源代码:
import java.util.Arrays;
import java.util.Scanner;
public class S2_5_5 {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
System.out.println("请输入数组的行数n与列数m:");
int n = reader.nextInt();
int m = reader.nextInt();
int[][] src = new int[n][m];
int[][] dst = new int[m][n];
System.out.printf("请输入一个%d*%d的数组:\n",n,m);
for(int i = 0; i < src.length;i++)
{
for(int j = 0; j < src[i].length; j++)
{
src[i][j] = reader.nextInt();
}
}
for(int i = 0; i < dst.length; i++)
{
for(int j = 0; j < dst[i].length; j++)
{
dst[i][j] = src[j][i];
}
}
System.out.println("转制后的二位数组为:");
//System.out.println(Arrays.deepToString(dst));
for(int i = 0; i < dst.length; i++)
{
for(int j = 0; j < dst[i].length; j++)
{
System.out.print(dst[i][j] + " ");
}
System.out.println();
}
}
}
列出测试数据和实验结果截图:
三、实验总结
- 完成上述实验,我更熟地掌握了Java的基础语法,包括:
- Java关于标识符的命名规则,Java标识符可以字母、数字、下划线和美元符构成,且数字不能开头。类名用大驼峰,变量名、方法名用小驼峰。
- 掌握了8种基本数据类型int,byte,short,char,long,float,double,Boolean的使用情况和相应的类型转换方法。
- 我熟悉了各种运算符包括自增、自减、三目运算符等的优先级和结合方向。
- 我掌握了控制结构的3种语句,包括if、switch选择语句、while、do-while、for、for-each循环语句。
- 我掌握了Java输入数据的基本方法:Scanner。我可以熟练地通过调用Scanner对象输入各种数据类型的数据进行编程。
- 我掌握了方法的定义和调用格式,能正确写出Java定义方法的语法。也理解了方法调用和参数传递的原理,包括值传递和址传递。
- 我掌握了方法的重载,重载的条件是方法的名称必须相同,参数列表必须不同,返回值类型不做要求。
- 我也理解了方法的递归调用,递归就是方法自己调用自己,会在虚拟机栈中建立栈帧。
-
在与数组相关的实验中,我掌握了数组的定义、动态和静态初始化格式。我能通过for循环对一维数组和二维数组进行遍历,能完成数组的转置、数组的排序,构建引用类型的数组。也可以熟练通过Array工具类,直接调用Arrays中的方法进行数组排序和逆序。
- 在编程中我没有遇到难以解决的问题。遇到唯一的问题是在求完数时,本来需要对一个数进行试除,然后计算该数的所有因子之和。我在for循环判断试除因子范围时,将i的界限写成了i <=Math.sqrt(n),但输出结果有误(什么也没输出)。通过调试,后来我将i 改为 i <= n/2,就能正确输出结果了。这是因为我一开始没有意识到因子有对称性这个特点。
- 最后,通过实验,我对基础算法进行了很好的复习。