入门篇-其之八-常用类的简单使用

本文中使用到的工具是Intellij IDEA和JDK 8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程点我查看安装Intellij IDEA教程

一、控制台输入类Scanner

假设今天我想在瓜摊买一个西瓜(西瓜的重量是10斤),西瓜两块钱一斤,此时使用Java程序代码如下:

图片来源于网络,侵删

/**
 * 计算西瓜的价格
 *
 * @author iCode504
 * @date 2023-10-31
 */
public class MyWatermelonDemo1 {
    public static void main(String[] args) {
        int price = 2;      // 西瓜的单价
        int weight = 10;    // 西瓜的重量(公斤)
        int totalPrice = price * weight;    // 购买价格
        System.out.println("西瓜的价格是: " + totalPrice + "元");
    }
}

运行结果:

image-20231031141401505

然而现实生活中西瓜的单价和重量是变化的,我们需要手动输入单价和重量,再将输入的内容进行计算。如果在Java代码中实现这一功能,就需要使用到控制台输入类Scanner

要想执行输入操作,需要创建一个Scanner类型的对象,Scanner类位于java.util包中(包的概念后续会讲到),需要我们在类的上方手动导入。

import java.util.Scanner;

导入完成后,我们就可以在main方法中创建Scanner类型的对象了,在Scanner的构造器中还需要传入一个参数System.in表示从控制台输入,代码如下:

Scanner scanner = new Scanner(System.in);

此时我们就完成了scanner对象的创建,此时我们就可以调用Scanner类中的方法了,由于我们定义的是int类型的变量,此时我们就可以使用Scanner类中的nextInt()方法实现输入功能,例如:

int price = scanner.nextInt();

在控制台输入的内容就会赋值给当前变量并且可以参与后续的运算。

以下是解决上述方案的完整代码:

import java.util.Scanner;       // 要想使用Scanner类,就必须要在类的上方导入

/**
 * 使用Scanner类实现手动输入,然后计算结果
 *
 * @author iCode504
 * @date 2023-10-31
 */
public class MyWatermelonDemo2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入西瓜的单价: ");
        int price = scanner.nextInt();
        System.out.println("请输入西瓜的重量(按斤计算): ");
        int weight = scanner.nextInt();
        int totalPrice = price * weight;
        System.out.println("西瓜的单价是" + price + "元, 重量是" + weight + "斤, 价格是" + totalPrice + "元");
    }
}1

运行结果:

23103001

除了booleanchar类型以外,其他七种数据类型都可以调用nextXxx()方法,使用方式和上述过程完全相同:

基本数据类型 调用方法
byte nextByte()
short nextShort()
int nextInt()
long nextLong()
float nextFloat()
double nextDouble()

除了能输入数字以外,Scanner类还提供了字符串输入的方法:next()nextLine()。这两个方法都能在控制台输入字符串,二者的区别是:

  • next()方法读取字符串,直到遇到空格、制表符Tab和回车Enter为止,如果这三个符号后面还存在其他字符,next()方法都会省略。
  • nextLine()方法读取字符串,直到遇到回车Enter为止。即使当前行存在空格,也能正常输出。

以下是两种方法的使用案例:

import java.util.Scanner;

/**
 * next()方法和nextLine()方法的区别
 *
 * @author iCode504
 * @date 2023-10-31
 */
public class MyWatermelonDemo3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请使用nextLine()方法输入内容,按回车键结束: ");
        String strValue1 = scanner.nextLine();
        System.out.println("使用nextLine()输出结果是: " + strValue1);

        System.out.println("请使用next()方法输入内容,按回车键结束: ");
        String strValue2 = scanner.next();
        System.out.println("使用next()输出结果是: " + strValue2);
    }
}

运行结果:

image-20231031152342510

二、数学类Math

在初高中我们学习的一些数学函数在Java中同样使用。这些数学函数都在Math类中。

2.1 绝对值、两数的最小值和最大值

绝对值的概念:正数的绝对值是其本身,0的绝对值是0,负数的绝对值是其相反数。在Math类中,我们可以调用静态方法Math.abs(number)来获取number的绝对值,其中number的类型只能是intlongfloatdouble中的一种。

两数的最小值可以调用Math.min(number1, number2),如果number1 > number2,那么得到的结果是number2,反之,得到的结果是number1

两数的最大值可以调用Math.max(number1, number2),如果number1 > number2,那么得到的结果是number1,反之,得到的结果是number2

其中number1number2需要保证是intlongfloatdouble中的一种。

以下是示例代码:

/**
 * 绝对值abs()、两数最小值min()、两数最大值max()的应用
 *
 * @author iCode504
 * @date 2023-10-31
 */
public class MathDemo1 {
    public static void main(String[] args) {
        // 取绝对值
        int intValue1 = 20;
        int result1 = Math.abs(intValue1);
        double doubleValue1 = -2.45;
        double result2 = Math.abs(doubleValue1);
        float floatValue1 = 0.0f;
        float result3 = Math.abs(floatValue1);

        System.out.println("----------取绝对值----------");
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);

        // 两数取最小值、最大值
        int intValue2 = 30;
        int intValue3 = 40;
        int result4 = Math.min(intValue2, intValue3);
        int result5 = Math.max(intValue2, intValue3);
        System.out.println("----------取最小值、最大值----------");
        System.out.println("result4 = " + result4);
        System.out.println("result5 = " + result5);
    }
}

运行结果:

image-20231031182425433

那么Math.abs()Math.min()Math.max()为什么只支持intlongfloatdouble四种类型。我们使用Ctrl和鼠标左键点击abs()方法进入源码:

23103002

此时按Alt7键,会列举出当前类所有的方法,此时我们在列表中直接输入abs搜索,发现只有四个结果:

image-20231031183335294

此时我们依次点击进入查看源码,发现它们支持的数据类型只有intlongfloatdouble。以int类型的abs(int)方法为例,我们发现方法内部就是一个三元运算符组成的表达式:

public static int abs(int a) {
    return (a < 0) ? -a : a;
}

如果a < 0,那么得到的结果就是其相反数-a,反之,0和正数得到的绝对值就是其本身。longfloatdoubleabs()方法亦同理。

此时我们可以按照上述的方式找到minmax方法,发现二者也是仅支持intlongfloatdouble,方法列表如下:

image-20231031184018449

image-20231031184031326

min(int, int)方法为例,此时我们点击查看源码,发现这个方法体中也用到了三元表达式:

public static int min(int a, int b) {
    return (a <= b) ? a : b;
}

如果a小于等于b,那么最小值就是a,反之为b

而浮点类型的min(double, double)方法源码则在此基础上做了进一步判断:

public static double min(double a, double b) {
    if (a != a)
        return a;   // a is NaN
    if ((a == 0.0d) &&
        (b == 0.0d) &&
        (Double.doubleToRawLongBits(b) == negativeZeroDoubleBits)) {
        // Raw conversion ok since NaN can't map to -0.0.
        return b;
    }
    return (a <= b) ? a : b;
}

如果参数a的值是NaN(NaN是一个特殊的浮点类型的数值,表示无效或者无意义的数值结果,例如:0.0 / 0.0得到的结果没有意义,其结果就是NaN),由于NaN是无意义的结果,因此两个NaN的值比较结果就是false。源码中的第一个if判断就是针对NaN结果的判断,如果a的确是NaN,那么比较的结果没有意义,返回的结果也就是变量a本身的值NaN。

第二个比较主要是针对a的值是0.0,b的值是-0.0的情况,0.0在默认情况下无论前面加上正负号都是0.0,第三个条件中Double.doubleToRawLongBits()方法是将当前按浮点数转换成64位的long类型数,negativeZeroDoubleBits就是上述方法默认的-0.0转换成long类型的数字,如果此时Double.doubleToRawLongBits(b)得到的结果和negativeZeroDoubleBits的值完全相同,那么得到的结果是b的值-0.0。

如果上述两个条件都不符合,那么就使用三元运算符进行比较,如果a小于等于b,返回值是a,反之为b

2.2 数学常量\(\pi\)\(e\)

数学常量是指在数学领域中经常使用的,具有特定数值的量。在中学阶段,我们接触到的两个常量是圆周率\(\pi\)(3.1415926...)和自然对数\(e\)(2.7182818...)。这两个常量在Java的Math类有存储,我们只需要调用Math.PI即可获取\(\pi\)值,调用Math.E即可获取\(e\)值。

/**
 * 数学常量:圆周率和自然对数
 *
 * @author iCode504
 * @date 2023-11-02
 */
public class MathDemo2 {
    public static void main(String[] args) {
        System.out.println("圆周率的值是: " + Math.PI);
        System.out.println("自然对数的值是: " + Math.E);
    }
}

运行结果:

image-20231102070010918

从运行结果中我们可以发现,Math.PIMath.E只输出了小数点后的一部分,这是因为在Math类中关于PIE使用的是double类型,由于double的精度只有15位,因此输出结果保留了小数点后15位。

E和PI的源码

2.3 三角函数

Math类中定义了很多和三角函数相关方法,所有的三角函数得到的结果都是double类型,这里选择了3个具有代表性的三角函数:

方法 说明
sin(a) 正弦函数
cos(a) 余弦函数
tan(a) 正切函数

和数学上的使用基本上一样,我们只需要确定a的值即可。例如:\(sin(\frac{\pi}{6})=0.5,cos(\frac{\pi}{3}=0.5),tan(\frac{\pi}{4})=1\),此时我们可以使用程序来检验一下:

/**
 * 三角函数的使用
 *
 * @author iCode504
 * @date 2023-11-02
 */
public class MathDemo3 {
    public static void main(String[] args) {
        // 弧度使用Math.PI来表示
        double sinResult = Math.sin(Math.PI / 6);
        double cosResult = Math.cos(Math.PI / 3);
        double tanResult = Math.tan(Math.PI / 4);

        System.out.println("sinResult = " + sinResult);
        System.out.println("cosResult = " + cosResult);
        System.out.println("tanResult = " + tanResult);
    }
}

运行结果:

image-20231102073800307

但是从运行结果中我们可以发现得到的结果和预期的值相差“一点点”,出现上述情况的原因主要有两点:首先,计算机本身处理浮点类型的数值就不准确。另外,Math.PI的值是小数点的后15位,做不到十分精确。因此得到的结果和期望值存在误差。

2.4 指数函数和对数函数

Math类中定义了如下常用的指数函数和对数函数:

方法名 说明
sprt(a) 求a的平方根
pow(a, b) 求a的b次方,即\(a^b\)
exp(a) 求自然对数\(e\)的a次方,即\(e^a\)
log(a) 求以自然对数\(e\)为底,a的对数,即\(ln(a)\)
log10(a) 求以10为底,a的对数,即\(log_{10}a\)

以下是这些数学函数在代码中的应用:

/**
 * 指数函数、对数函数的使用
 *
 * @author iCode504
 * @date 2023-11-03
 */
public class MathDemo4 {
    public static void main(String[] args) {
        int number1 = 49;
        double result1 = Math.sqrt(number1);    // 求number1的平方根
        int number2 = 3;
        int number3 = 4;
        double result2 = Math.pow(number2, number3);    // 求number2的number3次方
        double result3 = Math.exp(3);       // 求e的3次方
        double result4 = Math.log(2 * Math.E);      // 求以e为底,2e的对数
        double result5 = Math.log10(100);       // 求以10为底,100的对数

        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);
        System.out.println("result4 = " + result4);
        System.out.println("result5 = " + result5);
    }
}

运行结果:

image-20231104105718121

2.5 数字的舍入操作

在进行数学运算时,我们可能需要对小数进行舍入操作(例如:四舍五入),Math类为我们提供了以下四种关于小数舍入的方法:

方法名 返回类型 说明
ceil(x) double 获取大于或等于当前数值的最小整数
floor(x) double 获取小于或等于当前数值最大整数
rint(x) double 获取当前数值最接近的整数,如果有两个相同接近的整数,取偶数
round(x) double 四舍五入,舍入数字以第一位小数为基准

以下是这些数学函数在代码中的应用:

/**
 * 舍入函数的使用
 *
 * @author iCode504
 * @date 2023-11-03
 */
public class MathDemo5 {
    public static void main(String[] args) {
        double number1 = 2.46;
        double number2 = -2.34;

        // 获取大于或等于当前数值的最小整数
        double result1 = Math.ceil(number1);
        double result2 = Math.ceil(number2);
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);

        System.out.println("--------------------");
        // 获取小于或等于当前数值最大整数
        double result3 = Math.floor(number1);
        double result4 = Math.floor(number2);
        System.out.println("result3 = " + result3);
        System.out.println("result4 = " + result4);

        System.out.println("--------------------");
        // 获取当前数值最接近的整数,如果有两个相同接近的整数,取偶数
        double result5 = Math.rint(number1);
        double result6 = Math.rint(number2);
        double result7 = Math.rint(5.5);
        System.out.println("result5 = " + result5);
        System.out.println("result6 = " + result6);
        System.out.println("result7 = " + result7);

        System.out.println("--------------------");
        // 四舍五入,舍入数字以第一位小数为基准
        double result8 = Math.floor(number1);
        double result9 = Math.floor(number2);
        System.out.println("result7 = " + result8);
        System.out.println("result8 = " + result9);
    }
}

运行结果:

image-20231104105840264

2.6 随机数

Math类中为我们提供了一个获取随机数的方法random(),它默认在\([0,1)\)范围内生成小数。我们可以利用这个范围,生成任意范围的数字。

例如:利用Math.random()所给的范围,生成\([15, 60]\)之间的随机数。

首先,整数范围\([15, 60]\)可以等价写成\([15, 61)\)

再获取范围差:\(61 - 15 = 46\)

利用不等式的性质,将原有的\([0, 1)\)乘以46得到\([0, 46)\),再将现有的范围再加上15,即可获得目标范围:\([15, 61)\)

总结:从\([0,1)\)转换到\([15,61)\)先乘以46,再加15即可。

以下上述案例在Java代码中的实现:

/**
 * Math.random()生成随机数
 *
 * @author iCode504
 * @date 2023-11-03
 */
public class MathDemo6 {
    public static void main(String[] args) {
        // 每次生成的随机数值都不相同
        double result1 = Math.random();
        double result2 = Math.random();
        double result3 = Math.random();

        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);
        System.out.println("--------------------");

        // 由于生成的是[15,60]之间的整数,需要将计算结果强制转换int类型
        int randomNumber1 = (int) (Math.random() * 46 + 15);
        int randomNumber2 = (int) (Math.random() * 46 + 15);
        int randomNumber3 = (int) (Math.random() * 46 + 15);

        System.out.println("randomNumber1 = " + randomNumber1);
        System.out.println("randomNumber2 = " + randomNumber2);
        System.out.println("randomNumber3 = " + randomNumber3);
    }
}

每次得到的结果都不相同:

231104005

三、随机数类Random

前面我们学过Math.random()方法来生成随机数,但是这个方法存在一个局限是它默认生成的范围是\([0,1)\)之间的浮点数值,如果需要更大范围的随机数需要进行一定的计算并且需要进行强制类型转换,可能会导致代码可读性变低。

而接下来要提到的Random类可以避免强制类型转换的问题,并且包含Math.random()方法所不包含的一些特性。

3.1 随机数相关的概念

伪随机数:伪随机数是计算机利用特定的算法计算出来的\([0,1)\)均匀分布的随机序列。虽然伪随机数并不是真正的随机数,但是它们具有类似随机数的统计特征:均匀性和独立性。在计算伪随机数时,如果使用的初始值(也称作随机数种子)不变,那么生成伪随机数的序列也不会改变。伪随机数可以使用程序大量生成。

随机数种子:随机数种子是在伪随机数生成器中用于生成伪随机数的初始数值,随机数种子一般是数字。在伪随机数生成器中,给定相同的种子值,将会生成相同的伪随机数的序列。

3.2 随机数类Random的使用

和前面讲过的Scanner类一样,Random类也在java.util包中。创建随机数的方法如下:

1. 在类的上方导入Random类:

import java.util.Random;

2. 创建一个Random对象:有两种方式:一种是给定随机数,另外一种就是不给随机数:

构造方法 说明
Random(long) 传入一个long类型的随机数种子,后续生成一个固定的随机数序列
Random() 如果构造方法中没有随机数,计算机会给定一个随机数种子。当然,后续生成的随机数列就不是固定的
Random random1 = new Random(20);		// 给定一个随机数种子20,后续会生成一个固定的随机数数列
Random random2 = new Random();			// 不直接给定随机数种子,让计算机自己分配一个种子,生成一个不固定的随机数数列

3. 根据要生成的随机数类型,调用随机数方法,支持整数类型(intlong)、浮点型(floatdouble)和布尔类型(boolean)。方法列表如下:

方法名称 说明
nextInt() 生成int范围内的随机数
nextInt(int) 生成1到int最大值范围内(不包含int最大值)的随机数
nextLong() 生成long范围内的随机数
nextFloat() 生成float范围内的随机数
nextDouble() 生成double范围内的随机数
nextBoolean() 随机生成truefalse

以下是上述方法的使用:

import java.util.Random;

/**
 * 随机数的应用
 *
 * @author iCode504
 * @date 2023-11-04
 */
public class RandomDemo1 {
    public static void main(String[] args) {
        // 不使用随机数种子
        Random random1 = new Random();
        int result1 = random1.nextInt();            // 生成int范围内的随机整数
        int result2 = random1.nextInt(60);      // 生成1到60范围内的随机整数
        long result3 = random1.nextLong();          // 生成long范围内的随机整数
        float result4 = random1.nextFloat();        // 生成float范围内的随机整数
        double result5 = random1.nextDouble();      // 生成double范围内的随机整数
        boolean result6 = random1.nextBoolean();    // 生成true或false

        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        System.out.println("result3 = " + result3);
        System.out.println("result4 = " + result4);
        System.out.println("result5 = " + result5);
        System.out.println("result6 = " + result6);

        System.out.println("--------------------");
        // 使用随机数种子生成固定序列
        Random random2 = new Random(20);
        for (int i = 0; i < 5; i++) {
            int randomValue = random2.nextInt();
            System.out.println("randomValue" + (i + 1) + " = " + randomValue);
        }
    }
}

运行结果:

231104002

我们也可以使用Random解决上述生成随机数问题:

import java.util.Random;

/**
 * 使用Random类生成[15, 60]范围内的整数
 *
 * @author iCode504
 * @date 2023-11-04
 */
public class RandomDemo2 {
    public static void main(String[] args) {
        Random random = new Random();   // 不设置随机种子
        int result = random.nextInt(46) + 15;
        System.out.println("result = " + result);
    }
}

多运行几次程序,我们发现生成的随机数确实在\([15,60]\)范围内:

231104003

以上是使用Random类生成随机数,相对于Math.random()而言,生成随机数可以省去强制类型转换,相对方便了一些。

Random类是一个方便实用的工具类,它提供了各种方法来获取不同类型和范围的随机数,适用于各种模拟、游戏、密码学等方面应用。通过使用Random类,开发人员可以轻松地生成具有良好随机性和不可预测性的伪随机数,从而提高应用程序的灵活性和效率。

posted @ 2023-11-06 12:52  iCode504  阅读(360)  评论(0编辑  收藏  举报