Java基础（第二章）

## 1. 如何打印输出

**这一章中，我们使用面向过程（C语言的方式）来了解Java的基本语法。虽然说是和面向对象无关，但是我们需要一个函数入口（main）函数，这个函数上一章讲过，有固定的格式，且这个函数必须定义在一个Public类中。**

### 1.1. ComputeArea.java

先来看下面这个例子：

ComputeArea.java

```java
public class ComputeArea {
    public static void main(String[] args) {
        double radius = 0.0; // Declare radius
        double area; // Declare area

        // Assign a radius
        radius = 20.0; // New value is radius

        // Compute area
        area = radius * radius * 3.14159;

        // Display results
        System.out.println("The area for the circle of radius " + radius + " is " + area);
    }
}
```

2-10行的语法结构和C完全一致，这里我门只关心13行的代码。从第13行代码看出什么？首先是 System.out.println 这显然是一个函数，表示要打印一个字符串。

**按住ctl键盘，然后鼠标左键点击第13行的println，Eclipse会打开一个新的文件（PrintStream.class），并定位到println这个函数的位置。Eclipse这种特性叫做代码跳转，可以方便的浏览代码。**

在PrintStream.class 最顶部，发现这个文件的包是 java.io。想一想这个包为什么不是System.out？你可以继续点中out来参看源代码，发现out的定义是null。Java缺省初始化out是一个PrintStream对象，关于这一点超出了我们需要讨论的范围，大家只需要记住 System.out.println 类似C的println函数就可以了。System 是Java虚拟机的自带的一个包（类似C的标准库），也叫做API。

**注意这个文件是PrintStream.class，是编译后的binarycode，照理说应该看不到源代码的。主流的IDE可以根据Java的API自动查找源代码，便于我们的开发。**

尝试在代码中输入System.out.prin Eclipse会提示很多函数，界面如下：

![](img/20230303153149.png)

我们发现有很多print函数，函数名是一样的，但是参数不一样。这种方式是可行的，叫做函数重载，这种情况下，根据输入参数的类型或者是数量来匹配同名函数的一个确定版本。后面会专门讲到函数重载的技术。

另外我们还注意到有一个叫做printf的函数，这个函数和C的printf函数是一样的。

当你选择一个函数，右边会出现这个函数的英文解释。

**在Java中的字符串是用双引号来标识的，这点和C区别非常大；C语言的字符串是一个数组，Java是一种特定的数据类型（类、对象）。关于字符串，后面会详细讲解。这里只需要掌握字符串的表示（双引号），字符串的连接（+运算符）就可以了。**

再总结一下我们学的的：

1. main函数的固定写法；
2. main函数必须在一个public类中定义；
3. 字符串的表示和连接；
4. 如何使用打印函数。

### 1.2. 问题

1. main函数前面的static是什么意思？
2. main函数的参数 String\[\] args 是什么意思？

后面回讲到上述两个问题，你可以先研究一下。

## 2. 如何读取键盘输入

上面讲到了输出打印，现在来看看如何读取输入。在学习Java如何读取输入之前，我们回忆以下C是如何读取输入的？

C使用scanf函数配合一套字符串规则实现各种数据类型的输入和打印；Java的输入会更直观，接下来我们看一个简单的例子：

### 2.1. ComputeAreaWithConsoleInput.java

```java
import java.util.Scanner; // Scanner is in the java.util package

public class ComputeAreaWithConsoleInput {
  public static void main(String[] args) {
    // Create a Scanner object
    Scanner input = new Scanner(System.in); 

    // Prompt the user to enter a radius
    System.out.print("Enter a number for radius: ");
    double radius = input.nextDouble();

    // Compute area
    double area = radius * radius * 3.14159;
    // Display result
    System.out.println("The area for the circle of radius " +
      radius + " is " + area);
  } 
}
```

注意第一行的 import ..... 这个语句，这是把Scanner所在的包引入进来，相当于C的include语句。如果不写第一行，Eclipse会在地6行的 Scanner位置错误（红色下划线），这是只需要把鼠标移动到 Scanner 上面，会出现quike fix 的建议，如下图。这时只需要点击：Import 'Scanner' (java.util) 就可以了，Eclipse会自动加上import语句。

![](img/20230303153514.png)

这个例子稍微复杂一些，要读取键盘输入，这里主要使用的是Scanner类。类是一种抽象定义，例如我们前一章定义的圆，只需要说明圆有什么属性、有什么函数；但是并没有对其具体的属性进行赋值；也就是说圆这个类是所有圆对象（实体）的抽象描述。当对类进行实例化（使用 new 关键字），对其分配内存空间和对相应的属性进行初始化后，就得到了圆的对象（实体）。这点有点像C中的结构体指针，如果要生成一个可以操作的结构体，需要使用malloc函数分配结构体指针变量地址一样；同样 new关键字对应malloc函数。

因此第6行的Scanner是一个类，使用 new 关键字生成一个对象并把引用地址（指针）赋值给 input 这个变量。

**注意：Scanner即是一个类型，也是一个函数；不过这个函数必须和 new 关键字配合使用，这个函数叫做构造函数，当一个类实例化的时候用作进行一些初始化操作；构造函数是可以带参数的；例如这里的System.in。关于构造函数，后面有专门的内容进行讨论，这里只需要进行初步的了解。**

为Scanner类的构造函数传递一个叫做 System.in 的参数，表示Scanner从系统标准输入（一般是键盘）中读取数据。类似C的输入/输出重定向一样，Java也可以从文件读取数据，这个以后再讲。

当生成类型为Scanner的对象input后，就可以直接在input上使用相应的函数读取不同类型的数据了。例如nextDouble()函数是从键盘读取一个double类型的值，直到遇到空格或者是回车（空格并不能使nextDouble函数返回，只有回车可以让nextDouble函数返回）。程序在运行到nextDouble这个函数的时候，会等待，直到输入回车后把值读取到radius变量。

其他代码比较简单，就不解释了。

如果需要读取整数，字符串等其他类型的值如何办？看看input这个对象的其他函数，只需要在input后面输入点（.）后，Eclipse会提示input对象支持的所有函数，当然有很多我们不关心的函数。我们一般只关心hasNext开头的函数和next开头的函数。其中hasNext开头的函数是用来判断输入缓冲区中是否有下一个数据；next开头的是读取下一个数据。

**关于Scanner类的其他函数，以后会用到，有兴趣你们可以通过阅读在线文档先了解一下。**

## 3. 标识符、变量、常量

### 3.1. 与C语言一致的

1. Identifiers：标识符
2. Variables：变量
3. Declaring Variables：变量声明
4. Assignment Statements：赋值语句
5. Declaring and Initializing in One Step：变量声明与赋值用一行语句

上面这些和C保持一致，我们只看看常量的声明。

### 3.2. 常量的声明

Java常量声明与C完全不一样，想一想C的常量如何声明的？下面是Java常量的语法：

final datatype CONSTANTNAME = VALUE;  

例如：

```java
final double PI = 3.14;
final String ERROR="ERROR";
```

### 3.3. 命名规则

其实只要是符合标识符的规则就是合法的变量名、常量名、函数名、类名；但是为了阅读方便，大家有个对命名大小写的共识，一般是这样：

1. 常量：全部大写；
2. 变量：首字母小写；
3. 类名（接口名）：首字母大写，如果有多个单词，每个单词的首字母大写；
4. 函数名：首字母小写。

## 4. 基本数据类型

### 4.1. 数值类型

| 类型     | 存储大小（bit） | 说明  |
| ------ | --------- | --- |
| byte   | 8         |     |
| short  | 16        |     |
| int    | 32        |     |
| long   | 64        |     |
| float  | 32        |     |
| double | 64        |     |

C语言中有uint、ulong等带u开头的整形类型，Java中有吗？

**注意，Java的类型中所有小写开头的类型都对应一个大写开头的类型。例如int和Integer这两个类型是不同的，关于大写开头的类型我们以后再讲。这里只需要记住小写开头的类型叫做基本类型，与C的类型保持一致；大写开头的对应类型叫做封装类型（类）是面向对象的。**

Scanner对象有相应读取这些数据类型的函数，请自己尝试一下。

### 4.2. 数字运算

基本运算符、除法（整除与浮点除）、取余与C保持一致。

试一试，如果使用System.out输出一个浮点数，要求保留小数点后的位数应该如何操作。

### 4.3. 数值类型的字面量（Literals）

字面量的含义是语句中直接写入整形数值或者是浮点数值的时候，这个数值表达的数据类型或者是赋值规则。例如：

```java
        byte a = 100;
        byte b = 200;
        int c = 300;
        int d = 5000;
        float e = 33.3;
        double f = 33.3;
```

上述代码中，第二行代码是非法的；第五行代码也是非法的；为什么？

规则：

1. 对于整形赋值，只要字面量表达的数值不超过变量类型的取值范围就是合法的；
2. 对于带小数的字面量，缺省的类型是double，因此如果赋值给float的类型的变量会出错，正确的做法是在字面量后面加上F。

```java
float e = 33.3F;
```

科学计数法：例如：1.23456e-2

### 4.4. 扩展赋值语法：Augmented Assignment Operators

![](img/20230303162645.png)

和C语言保持一致。

### 4.5. 自增、自减操作

![](img/20230303162710.png)

与C保持一致

### 4.6. 数值类型转换

隐式转换：首先看被赋值的变量；再看表达式。表达式向高精度看齐，最终符合被赋值变量的精度。

包括计算时的精度匹配以及强制类型转换，保持与C一致。

## 5. 本章重点

1. 如何打印输出：有很多同名的打印函数（函数重载overload）；print，println，printf函数；与C有什么区别；
2. 字符串与其他类型连接通过+号；
3. 如何读取键盘输入，以及赋值。注意不同的类型使用不同的函数；
4. 如何判断是否还有没有读取的数据（hasNext函数XXX）；
5. 基本数据类型：全部都是小写，以后会学习对应大写的包装类型；都是有符号的；
6. 如何声明常量（final）；
7. 类型转换以及规则。
